Merge remote-tracking branch 'origin/topic/johanna/netcontrol'

BIT-1550 #merged

* origin/topic/johanna/netcontrol: (72 commits)
  Update baselines and news
  Move prefixtable back to all IPv6 internal handling.
  NetControl: Add functions to search for rules affecting IPs/subnets
  Add check_subnet bif that allows exact membership test for subnet tables.
  Rewrite internal handling of rules.
  Add bif that allows searching for all matching subnets in table.
  Add signaling of succesful initialization of plugins to NetControl.
  Add rule hooks to the acld plugin.
  Add new logfiles for shunting and drops to netcontrol
  Extend NetControl logging and fix bugs.
  Update OpenFlow API and events.
  small acld plugin fix
  Revert "introduce &weaken attribute"
  Fix crash when printing type of recursive structures.
  Testcase for crash when a record contains a function referencing a record.
  Rename Pacf to NetControl
  fix acld plugin to use address instead of subnet (and add functions for conversion)
  implement quarantine
  miscelaneous missing bits and pieces
  Acld implementation for Pacf - Bro side.
  ...
This commit is contained in:
Robin Sommer 2016-03-11 14:27:47 -08:00
commit 2233521de7
107 changed files with 6071 additions and 19 deletions

View file

@ -0,0 +1,3 @@
@load ./ryu
@load ./log
@load ./broker

View file

@ -0,0 +1,95 @@
##! OpenFlow plugin for interfacing to controllers via Broker.
@load base/frameworks/openflow
@load base/frameworks/broker
module OpenFlow;
export {
redef enum Plugin += {
BROKER,
};
## Broker controller constructor.
##
## host: Controller ip.
##
## host_port: Controller listen port.
##
## topic: broker topic to send messages to.
##
## dpid: OpenFlow switch datapath id.
##
## Returns: OpenFlow::Controller record
global broker_new: function(name: string, host: addr, host_port: port, topic: string, dpid: count): OpenFlow::Controller;
redef record ControllerState += {
## Controller ip.
broker_host: addr &optional;
## Controller listen port.
broker_port: port &optional;
## OpenFlow switch datapath id.
broker_dpid: count &optional;
## Topic to sent events for this controller to
broker_topic: string &optional;
};
global broker_flow_mod: event(name: string, dpid: count, match: ofp_match, flow_mod: ofp_flow_mod);
global broker_flow_clear: event(name: string, dpid: count);
}
global broker_peers: table[port, string] of Controller;
function broker_describe(state: ControllerState): string
{
return fmt("Broker-%s:%d-%d", state$broker_host, state$broker_port, state$broker_dpid);
}
function broker_flow_mod_fun(state: ControllerState, match: ofp_match, flow_mod: OpenFlow::ofp_flow_mod): bool
{
BrokerComm::event(state$broker_topic, BrokerComm::event_args(broker_flow_mod, state$_name, state$broker_dpid, match, flow_mod));
return T;
}
function broker_flow_clear_fun(state: OpenFlow::ControllerState): bool
{
BrokerComm::event(state$broker_topic, BrokerComm::event_args(broker_flow_clear, state$_name, state$broker_dpid));
return T;
}
function broker_init(state: OpenFlow::ControllerState)
{
BrokerComm::enable();
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.
}
event BrokerComm::outgoing_connection_established(peer_address: string, peer_port: port, peer_name: string)
{
if ( [peer_port, peer_address] !in broker_peers )
# ok, this one was none of ours...
return;
local p = broker_peers[peer_port, peer_address];
controller_init_done(p);
delete broker_peers[peer_port, peer_address];
}
# 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);
if ( [host_port, cat(host)] in broker_peers )
Reporter::warning(fmt("Peer %s:%s was added to NetControl acld plugin twice.", host, host_port));
else
broker_peers[host_port, cat(host)] = c;
return c;
}

View file

@ -0,0 +1,76 @@
##! OpenFlow plugin that outputs flow-modification commands
##! to a Bro log file.
@load base/frameworks/openflow
@load base/frameworks/logging
module OpenFlow;
export {
redef enum Plugin += {
OFLOG,
};
redef enum Log::ID += { LOG };
## Log controller constructor.
##
## dpid: OpenFlow switch datapath id.
##
## success_event: If true, flow_mod_success is raised for each logged line.
##
## Returns: OpenFlow::Controller record
global log_new: function(dpid: count, success_event: bool &default=T): OpenFlow::Controller;
redef record ControllerState += {
## OpenFlow switch datapath id.
log_dpid: count &optional;
## Raise or do not raise success event
log_success_event: bool &optional;
};
## The record type which contains column fields of the OpenFlow log.
type Info: record {
## Network time
ts: time &log;
## OpenFlow switch datapath id
dpid: count &log;
## OpenFlow match fields
match: ofp_match &log;
## OpenFlow modify flow entry message
flow_mod: ofp_flow_mod &log;
};
## Event that can be handled to access the :bro:type:`OpenFlow::Info`
## record as it is sent on to the logging framework.
global log_openflow: event(rec: Info);
}
event bro_init() &priority=5
{
Log::create_stream(OpenFlow::LOG, [$columns=Info, $ev=log_openflow, $path="openflow"]);
}
function log_flow_mod(state: ControllerState, match: ofp_match, flow_mod: OpenFlow::ofp_flow_mod): bool
{
Log::write(OpenFlow::LOG, [$ts=network_time(), $dpid=state$log_dpid, $match=match, $flow_mod=flow_mod]);
if ( state$log_success_event )
event OpenFlow::flow_mod_success(state$_name, match, flow_mod);
return T;
}
function log_describe(state: ControllerState): string
{
return fmt("Log-%d", state$log_dpid);
}
function log_new(dpid: count, success_event: bool &default=T): OpenFlow::Controller
{
local c = OpenFlow::Controller($state=OpenFlow::ControllerState($log_dpid=dpid, $log_success_event=success_event),
$flow_mod=log_flow_mod, $describe=log_describe, $supports_flow_removed=F);
register_controller(OpenFlow::OFLOG, cat(dpid), c);
return c;
}

View file

@ -0,0 +1,190 @@
##! OpenFlow plugin for the Ryu controller.
@load base/frameworks/openflow
@load base/utils/active-http
@load base/utils/exec
@load base/utils/json
module OpenFlow;
export {
redef enum Plugin += {
RYU,
};
## Ryu controller constructor.
##
## host: Controller ip.
##
## host_port: Controller listen port.
##
## dpid: OpenFlow switch datapath id.
##
## Returns: OpenFlow::Controller record
global ryu_new: function(host: addr, host_port: count, dpid: count): OpenFlow::Controller;
redef record ControllerState += {
## Controller ip.
ryu_host: addr &optional;
## Controller listen port.
ryu_port: count &optional;
## OpenFlow switch datapath id.
ryu_dpid: count &optional;
## Enable debug mode - output JSON to stdout; do not perform actions
ryu_debug: bool &default=F;
};
}
# Ryu ReST API flow_mod URL-path
const RYU_FLOWENTRY_PATH = "/stats/flowentry/";
# Ryu ReST API flow_stats URL-path
#const RYU_FLOWSTATS_PATH = "/stats/flow/";
# Ryu ReST API action_output type.
type ryu_flow_action: record {
# Ryu uses strings as its ReST API output action.
_type: string;
# The output port for type OUTPUT
_port: count &optional;
};
# The ReST API documentation can be found at
# https://media.readthedocs.org/pdf/ryu/latest/ryu.pdf
# Ryu ReST API flow_mod type.
type ryu_ofp_flow_mod: record {
dpid: count;
cookie: count &optional;
cookie_mask: count &optional;
table_id: count &optional;
idle_timeout: count &optional;
hard_timeout: count &optional;
priority: count &optional;
flags: count &optional;
match: OpenFlow::ofp_match;
actions: vector of ryu_flow_action;
out_port: count &optional;
out_group: count &optional;
};
# Mapping between ofp flow mod commands and ryu urls
const ryu_url: table[ofp_flow_mod_command] of string = {
[OFPFC_ADD] = "add",
[OFPFC_MODIFY] = "modify",
[OFPFC_MODIFY_STRICT] = "modify_strict",
[OFPFC_DELETE] = "delete",
[OFPFC_DELETE_STRICT] = "delete_strict",
};
# Ryu flow_mod function
function ryu_flow_mod(state: OpenFlow::ControllerState, match: ofp_match, flow_mod: OpenFlow::ofp_flow_mod): bool
{
if ( state$_plugin != RYU )
{
Reporter::error("Ryu openflow plugin was called with state of non-ryu plugin");
return F;
}
# Generate ryu_flow_actions because their type differs (using strings as type).
local flow_actions: vector of ryu_flow_action = vector();
for ( i in flow_mod$actions$out_ports )
flow_actions[|flow_actions|] = ryu_flow_action($_type="OUTPUT", $_port=flow_mod$actions$out_ports[i]);
# Generate our ryu_flow_mod record for the ReST API call.
local mod: ryu_ofp_flow_mod = ryu_ofp_flow_mod(
$dpid=state$ryu_dpid,
$cookie=flow_mod$cookie,
$idle_timeout=flow_mod$idle_timeout,
$hard_timeout=flow_mod$hard_timeout,
$priority=flow_mod$priority,
$flags=flow_mod$flags,
$match=match,
$actions=flow_actions
);
if ( flow_mod?$out_port )
mod$out_port = flow_mod$out_port;
if ( flow_mod?$out_group )
mod$out_group = flow_mod$out_group;
# Type of the command
local command_type: string;
if ( flow_mod$command in ryu_url )
command_type = ryu_url[flow_mod$command];
else
{
Reporter::warning(fmt("The given OpenFlow command type '%s' is not available", cat(flow_mod$command)));
return F;
}
local url=cat("http://", cat(state$ryu_host), ":", cat(state$ryu_port), RYU_FLOWENTRY_PATH, command_type);
if ( state$ryu_debug )
{
print url;
print to_json(mod);
event OpenFlow::flow_mod_success(state$_name, match, flow_mod);
return T;
}
# Create the ActiveHTTP request and convert the record to a Ryu ReST API JSON string
local request: ActiveHTTP::Request = ActiveHTTP::Request(
$url=url,
$method="POST",
$client_data=to_json(mod)
);
# Execute call to Ryu's ReST API
when ( local result = ActiveHTTP::request(request) )
{
if(result$code == 200)
event OpenFlow::flow_mod_success(state$_name, match, flow_mod, result$body);
else
{
Reporter::warning(fmt("Flow modification failed with error: %s", result$body));
event OpenFlow::flow_mod_failure(state$_name, match, flow_mod, result$body);
return F;
}
}
return T;
}
function ryu_flow_clear(state: OpenFlow::ControllerState): bool
{
local url=cat("http://", cat(state$ryu_host), ":", cat(state$ryu_port), RYU_FLOWENTRY_PATH, "clear", "/", state$ryu_dpid);
if ( state$ryu_debug )
{
print url;
return T;
}
local request: ActiveHTTP::Request = ActiveHTTP::Request(
$url=url,
$method="DELETE"
);
when ( local result = ActiveHTTP::request(request) )
{
}
return T;
}
function ryu_describe(state: ControllerState): string
{
return fmt("Ryu-%d-http://%s:%d", state$ryu_dpid, state$ryu_host, state$ryu_port);
}
# Ryu controller constructor
function ryu_new(host: addr, host_port: count, dpid: count): OpenFlow::Controller
{
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;
}