mirror of
https://github.com/zeek/zeek.git
synced 2025-10-13 03:58:20 +00:00
Rename Pacf to NetControl
This commit is contained in:
parent
eb9fbd1258
commit
0e213352d7
61 changed files with 498 additions and 498 deletions
13
scripts/base/frameworks/netcontrol/__load__.bro
Normal file
13
scripts/base/frameworks/netcontrol/__load__.bro
Normal file
|
@ -0,0 +1,13 @@
|
|||
@load ./types
|
||||
@load ./main
|
||||
@load ./plugins
|
||||
@load ./catch-and-release
|
||||
|
||||
# The cluster framework must be loaded first.
|
||||
@load base/frameworks/cluster
|
||||
|
||||
@if ( Cluster::is_enabled() )
|
||||
@load ./cluster
|
||||
@else
|
||||
@load ./non-cluster
|
||||
@endif
|
99
scripts/base/frameworks/netcontrol/catch-and-release.bro
Normal file
99
scripts/base/frameworks/netcontrol/catch-and-release.bro
Normal file
|
@ -0,0 +1,99 @@
|
|||
##! Implementation of catch-and-release functionality for NetControl
|
||||
|
||||
module NetControl;
|
||||
|
||||
export {
|
||||
## Stops all packets involving an IP address from being forwarded. This function
|
||||
## uses catch-and-release functionality, where the IP address is only dropped for
|
||||
## a short amount of time that is incremented steadily when the IP is encountered
|
||||
## again.
|
||||
##
|
||||
## a: The address to be dropped.
|
||||
##
|
||||
## t: How long to drop it, with 0 being indefinitly.
|
||||
##
|
||||
## location: An optional string describing where the drop was triggered.
|
||||
##
|
||||
## Returns: The id of the inserted rule on succes and zero on failure.
|
||||
global drop_address_catch_release: function(a: addr, location: string &default="") : string;
|
||||
|
||||
const catch_release_intervals: vector of interval = vector(10min, 1hr, 24hrs, 7days) &redef;
|
||||
}
|
||||
|
||||
function per_block_interval(t: table[addr] of count, idx: addr): interval
|
||||
{
|
||||
local ct = t[idx];
|
||||
|
||||
# watch for the time of the next block...
|
||||
local blocktime = catch_release_intervals[ct];
|
||||
if ( (ct+1) in catch_release_intervals )
|
||||
blocktime = catch_release_intervals[ct+1];
|
||||
|
||||
return blocktime;
|
||||
}
|
||||
|
||||
# This is the internally maintained table containing all the currently going on catch-and-release
|
||||
# blocks.
|
||||
global blocks: table[addr] of count = {}
|
||||
&create_expire=0secs
|
||||
&expire_func=per_block_interval;
|
||||
|
||||
function current_block_interval(s: set[addr], idx: addr): interval
|
||||
{
|
||||
if ( idx !in blocks )
|
||||
{
|
||||
Reporter::error(fmt("Address %s not in blocks while inserting into current_blocks!", idx));
|
||||
return 0sec;
|
||||
}
|
||||
|
||||
return catch_release_intervals[blocks[idx]];
|
||||
}
|
||||
|
||||
global current_blocks: set[addr] = set()
|
||||
&create_expire=0secs
|
||||
&expire_func=current_block_interval;
|
||||
|
||||
function drop_address_catch_release(a: addr, location: string &default=""): string
|
||||
{
|
||||
if ( a in blocks )
|
||||
{
|
||||
Reporter::warning(fmt("Address %s already blocked using catch-and-release - ignoring duplicate", a));
|
||||
return "";
|
||||
}
|
||||
|
||||
local block_interval = catch_release_intervals[0];
|
||||
local ret = drop_address(a, block_interval, location);
|
||||
if ( ret != "" )
|
||||
{
|
||||
blocks[a] = 0;
|
||||
add current_blocks[a];
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function check_conn(a: addr)
|
||||
{
|
||||
if ( a in blocks )
|
||||
{
|
||||
if ( a in current_blocks )
|
||||
# block has not been applied yet?
|
||||
return;
|
||||
|
||||
# ok, this one returned again while still in the backoff period.
|
||||
local try = blocks[a];
|
||||
if ( (try+1) in catch_release_intervals )
|
||||
++try;
|
||||
|
||||
blocks[a] = try;
|
||||
add current_blocks[a];
|
||||
local block_interval = catch_release_intervals[try];
|
||||
drop_address(a, block_interval, "Re-drop by catch-and-release");
|
||||
}
|
||||
}
|
||||
|
||||
event new_connection(c: connection)
|
||||
{
|
||||
# let's only check originating connections...
|
||||
check_conn(c$id$orig_h);
|
||||
}
|
66
scripts/base/frameworks/netcontrol/cluster.bro
Normal file
66
scripts/base/frameworks/netcontrol/cluster.bro
Normal file
|
@ -0,0 +1,66 @@
|
|||
@load ./main
|
||||
@load base/frameworks/cluster
|
||||
|
||||
module NetControl;
|
||||
|
||||
export {
|
||||
## This is the event used to transport add_rule calls to the manager.
|
||||
global cluster_netcontrol_add_rule: event(r: Rule);
|
||||
|
||||
## This is the event used to transport remove_rule calls to the manager.
|
||||
global cluster_netcontrol_remove_rule: event(id: string);
|
||||
}
|
||||
|
||||
## Workers need ability to forward commands to manager.
|
||||
redef Cluster::worker2manager_events += /NetControl::cluster_netcontrol_(add|remove)_rule/;
|
||||
## Workers need to see the result events from the manager.
|
||||
redef Cluster::manager2worker_events += /NetControl::rule_(added|removed|timeout|error)/;
|
||||
|
||||
|
||||
function activate(p: PluginState, priority: int)
|
||||
{
|
||||
# we only run the activate function on the manager.
|
||||
if ( Cluster::local_node_type() != Cluster::MANAGER )
|
||||
return;
|
||||
|
||||
activate_impl(p, priority);
|
||||
}
|
||||
|
||||
global local_rule_count: count = 1;
|
||||
|
||||
function add_rule(r: Rule) : string
|
||||
{
|
||||
if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
return add_rule_impl(r);
|
||||
else
|
||||
{
|
||||
if ( r$id == "" )
|
||||
r$id = cat(Cluster::node, ":", ++local_rule_count);
|
||||
|
||||
event NetControl::cluster_netcontrol_add_rule(r);
|
||||
return r$id;
|
||||
}
|
||||
}
|
||||
|
||||
function remove_rule(id: string) : bool
|
||||
{
|
||||
if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
return remove_rule_impl(id);
|
||||
else
|
||||
{
|
||||
event NetControl::cluster_netcontrol_remove_rule(id);
|
||||
return T; # well, we can't know here. So - just hope...
|
||||
}
|
||||
}
|
||||
|
||||
@if ( Cluster::local_node_type() == Cluster::MANAGER )
|
||||
event NetControl::cluster_netcontrol_add_rule(r: Rule)
|
||||
{
|
||||
add_rule_impl(r);
|
||||
}
|
||||
|
||||
event NetControl::cluster_netcontrol_remove_rule(id: string)
|
||||
{
|
||||
remove_rule_impl(id);
|
||||
}
|
||||
@endif
|
621
scripts/base/frameworks/netcontrol/main.bro
Normal file
621
scripts/base/frameworks/netcontrol/main.bro
Normal file
|
@ -0,0 +1,621 @@
|
|||
##! Bro's packet aquisition and control framework.
|
||||
##!
|
||||
##! This plugin-based framework allows to control the traffic that Bro monitors
|
||||
##! as well as, if having access to the forwarding path, the traffic the network
|
||||
##! forwards. By default, the framework lets evyerthing through, to both Bro
|
||||
##! itself as well as on the network. Scripts can then add rules to impose
|
||||
##! restrictions on entities, such as specific connections or IP addresses.
|
||||
##!
|
||||
##! This framework has two API: a high-level and low-level. The high-levem API
|
||||
##! provides convinience functions for a set of common operations. The
|
||||
##! low-level API provides full flexibility.
|
||||
|
||||
module NetControl;
|
||||
|
||||
@load ./plugin
|
||||
@load ./types
|
||||
|
||||
export {
|
||||
## The framework's logging stream identifier.
|
||||
redef enum Log::ID += { LOG };
|
||||
|
||||
# ###
|
||||
# ### Generic functions.
|
||||
# ###
|
||||
|
||||
# Activates a plugin.
|
||||
#
|
||||
# plugin: The plugin to acticate.
|
||||
#
|
||||
# priority: The higher the priority, the earlier this plugin will be checked
|
||||
# whether it supports an operation, relative to other plugins.
|
||||
global activate: function(p: PluginState, priority: int);
|
||||
|
||||
# ###
|
||||
# ### High-level API.
|
||||
# ###
|
||||
|
||||
## Stops all packets involving an IP address from being forwarded.
|
||||
##
|
||||
## a: The address to be dropped.
|
||||
##
|
||||
## t: How long to drop it, with 0 being indefinitly.
|
||||
##
|
||||
## location: An optional string describing where the drop was triggered.
|
||||
##
|
||||
## Returns: The id of the inserted rule on succes and zero on failure.
|
||||
global drop_address: function(a: addr, t: interval, location: string &default="") : string;
|
||||
|
||||
## Stops all packets involving an connection address from being forwarded.
|
||||
##
|
||||
## c: The connection to be dropped.
|
||||
##
|
||||
## t: How long to drop it, with 0 being indefinitly.
|
||||
##
|
||||
## location: An optional string describing where the drop was triggered.
|
||||
##
|
||||
## Returns: The id of the inserted rule on succes and zero on failure.
|
||||
global drop_connection: function(c: conn_id, t: interval, location: string &default="") : string;
|
||||
|
||||
## Stops forwarding a uni-directional flow's packets to Bro.
|
||||
##
|
||||
## f: The flow to shunt.
|
||||
##
|
||||
## t: How long to leave the shunt in place, with 0 being indefinitly.
|
||||
##
|
||||
## location: An optional string describing where the shunt was triggered.
|
||||
##
|
||||
## Returns: The id of the inserted rule on succes and zero on failure.
|
||||
global shunt_flow: function(f: flow_id, t: interval, location: string &default="") : string;
|
||||
|
||||
## Allows all traffic involving a specific IP address to be forwarded.
|
||||
##
|
||||
## a: The address to be whitelistet.
|
||||
##
|
||||
## t: How long to whitelist it, with 0 being indefinitly.
|
||||
##
|
||||
## location: An optional string describing whitelist was triddered.
|
||||
##
|
||||
## Returns: The id of the inserted rule on succes and zero on failure.
|
||||
global whitelist_address: function(a: addr, t: interval, location: string &default="") : string;
|
||||
|
||||
## Allows all traffic involving a specific IP subnet to be forwarded.
|
||||
##
|
||||
## s: The subnet to be whitelistet.
|
||||
##
|
||||
## t: How long to whitelist it, with 0 being indefinitly.
|
||||
##
|
||||
## location: An optional string describing whitelist was triddered.
|
||||
##
|
||||
## Returns: The id of the inserted rule on succes and zero on failure.
|
||||
global whitelist_subnet: function(s: subnet, t: interval, location: string &default="") : string;
|
||||
|
||||
## Redirects an uni-directional flow to another port.
|
||||
##
|
||||
## f: The flow to redirect.
|
||||
##
|
||||
## out_port: Port to redirect the flow to
|
||||
##
|
||||
## t: How long to leave the redirect in place, with 0 being indefinitly.
|
||||
##
|
||||
## location: An optional string describing where the redirect was triggered.
|
||||
##
|
||||
## Returns: The id of the inserted rule on succes and zero on failure.
|
||||
global redirect_flow: function(f: flow_id, out_port: count, t: interval, location: string &default="") : string;
|
||||
|
||||
## Quarantines a host by redirecting rewriting DNS queries to the network dns server dns
|
||||
## to the host. Host has to answer to all queries with its own address. Only http communication
|
||||
## from infected to quarantinehost is allowed.
|
||||
##
|
||||
## infected: the host to quarantine
|
||||
##
|
||||
## dns: the network dns server
|
||||
##
|
||||
## quarantine: the quarantine server running a dns and a web server
|
||||
##
|
||||
## t: how long to leave the quarantine in place
|
||||
##
|
||||
## Returns: Vector of inserted rules on success, empty list on failure.
|
||||
global quarantine_host: function(infected: addr, dns: addr, quarantine: addr, t: interval, location: string) : vector of string;
|
||||
|
||||
## Flushes all state.
|
||||
global clear: function();
|
||||
|
||||
# ###
|
||||
# ### Low-level API.
|
||||
# ###
|
||||
|
||||
###### Manipulation of rules.
|
||||
|
||||
## Installs a rule.
|
||||
##
|
||||
## r: The rule to install.
|
||||
##
|
||||
## Returns: If succesful, returns an ID string unique to the rule that can later
|
||||
## be used to refer to it. If unsuccessful, returns an empty string. The ID is also
|
||||
## assigned to ``r$id``. Note that "successful" means "a plugin knew how to handle
|
||||
## the rule", it doesn't necessarily mean that it was indeed successfully put in
|
||||
## place, because that might happen asynchronously and thus fail only later.
|
||||
global add_rule: function(r: Rule) : string;
|
||||
|
||||
## Removes a rule.
|
||||
##
|
||||
## id: The rule to remove, specified as the ID returned by :bro:id:`add_rule` .
|
||||
##
|
||||
## Returns: True if succesful, the relevant plugin indicated that ity knew how
|
||||
## to handle the removal. Note that again "success" means the plugin accepted the
|
||||
## removal. They might still fail to put it into effect, as that might happen
|
||||
## asynchronously and thus go wrong at that point.
|
||||
global remove_rule: function(id: string) : bool;
|
||||
|
||||
###### Asynchronous feedback on rules.
|
||||
|
||||
## Confirms that a rule was put in place.
|
||||
##
|
||||
## r: The rule now in place.
|
||||
##
|
||||
## plugin: The name of the plugin that put it into place.
|
||||
##
|
||||
## msg: An optional informational message by the plugin.
|
||||
global rule_added: event(r: Rule, p: PluginState, msg: string &default="");
|
||||
|
||||
## Reports that a rule was removed due to a remove: function() call.
|
||||
##
|
||||
## r: The rule now removed.
|
||||
##
|
||||
## plugin: The name of the plugin that had the rule in place and now
|
||||
## removed it.
|
||||
##
|
||||
## msg: An optional informational message by the plugin.
|
||||
global rule_removed: event(r: Rule, p: PluginState, msg: string &default="");
|
||||
|
||||
## Reports that a rule was removed internally due to a timeout.
|
||||
##
|
||||
## r: The rule now removed.
|
||||
##
|
||||
## i: Additional flow information, if supported by the protocol.
|
||||
##
|
||||
## plugin: The name of the plugin that had the rule in place and now
|
||||
## removed it.
|
||||
##
|
||||
## msg: An optional informational message by the plugin.
|
||||
global rule_timeout: event(r: Rule, i: FlowInfo, p: PluginState);
|
||||
|
||||
## Reports an error when operating on a rule.
|
||||
##
|
||||
## r: The rule that encountered an error.
|
||||
##
|
||||
## plugin: The name of the plugin that reported the error.
|
||||
##
|
||||
## msg: An optional informational message by the plugin.
|
||||
global rule_error: event(r: Rule, p: PluginState, msg: string &default="");
|
||||
|
||||
## Hook that allows the modification of rules passed to add_rule before they
|
||||
## are passed on to the plugins. If one of the hooks uses break, the rule is
|
||||
## ignored and not passed on to any plugin.
|
||||
##
|
||||
## r: The rule to be added
|
||||
global NetControl::rule_policy: hook(r: Rule);
|
||||
|
||||
## Type of an entry in the PACF log.
|
||||
type InfoCategory: enum {
|
||||
## A log entry reflecting a framework message.
|
||||
MESSAGE,
|
||||
## A log entry reflecting a framework message.
|
||||
ERROR,
|
||||
## A log entry about about a rule.
|
||||
RULE
|
||||
};
|
||||
|
||||
## State of an entry in the PACF log.
|
||||
type InfoState: enum {
|
||||
REQUESTED,
|
||||
SUCCEEDED,
|
||||
FAILED,
|
||||
REMOVED,
|
||||
TIMEOUT,
|
||||
};
|
||||
|
||||
## The record type which contains column fields of the PACF log.
|
||||
type Info: record {
|
||||
## Time at which the recorded activity occurred.
|
||||
ts: time &log;
|
||||
## Type of the log entry.
|
||||
category: InfoCategory &log &optional;
|
||||
## The command the log entry is about.
|
||||
cmd: string &log &optional;
|
||||
## State the log entry reflects.
|
||||
state: InfoState &log &optional;
|
||||
## String describing an action the entry is about.
|
||||
action: string &log &optional;
|
||||
## The target type of the action.
|
||||
target: TargetType &log &optional;
|
||||
## Type of the entity the log entry is about.
|
||||
entity_type: string &log &optional;
|
||||
## String describing the entity the log entry is about.
|
||||
entity: string &log &optional;
|
||||
## String with an additional message.
|
||||
msg: string &log &optional;
|
||||
## Logcation where the underlying action was triggered.
|
||||
location: string &log &optional;
|
||||
## Plugin triggering the log entry.
|
||||
plugin: string &log &optional;
|
||||
};
|
||||
|
||||
## Event that can be handled to access the :bro:type:`NetControl::Info`
|
||||
## record as it is sent on to the logging framework.
|
||||
global log_netcontrol: event(rec: Info);
|
||||
}
|
||||
|
||||
redef record Rule += {
|
||||
##< Internally set to the plugin handling the rule.
|
||||
_plugin_id: count &optional;
|
||||
};
|
||||
|
||||
global plugins: vector of PluginState;
|
||||
global plugin_ids: table[count] of PluginState;
|
||||
global rule_counter: count = 1;
|
||||
global plugin_counter: count = 1;
|
||||
global rules: table[string,count] of Rule; # Rules indexed by id and cid
|
||||
global id_to_cids: table[string] of set[count]; # id to cid
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
Log::create_stream(NetControl::LOG, [$columns=Info, $ev=log_netcontrol, $path="netcontrol"]);
|
||||
}
|
||||
|
||||
function entity_to_info(info: Info, e: Entity)
|
||||
{
|
||||
info$entity_type = fmt("%s", e$ty);
|
||||
|
||||
switch ( e$ty ) {
|
||||
case ADDRESS:
|
||||
info$entity = fmt("%s", e$ip);
|
||||
break;
|
||||
|
||||
case CONNECTION:
|
||||
info$entity = fmt("%s/%d<->%s/%d",
|
||||
e$conn$orig_h, e$conn$orig_p,
|
||||
e$conn$resp_h, e$conn$resp_p);
|
||||
break;
|
||||
|
||||
case FLOW:
|
||||
local ffrom_ip = "*";
|
||||
local ffrom_port = "*";
|
||||
local fto_ip = "*";
|
||||
local fto_port = "*";
|
||||
if ( e$flow?$src_h )
|
||||
ffrom_ip = cat(e$flow$src_h);
|
||||
if ( e$flow?$src_p )
|
||||
ffrom_port = fmt("%d", e$flow$src_p);
|
||||
if ( e$flow?$dst_h )
|
||||
fto_ip = cat(e$flow$dst_h);
|
||||
if ( e$flow?$dst_p )
|
||||
fto_port = fmt("%d", e$flow$dst_p);
|
||||
info$entity = fmt("%s/%s->%s/%s",
|
||||
ffrom_ip, ffrom_port,
|
||||
fto_ip, fto_port);
|
||||
break;
|
||||
|
||||
case MAC:
|
||||
info$entity = e$mac;
|
||||
break;
|
||||
|
||||
default:
|
||||
info$entity = "<unknown entity type>";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function rule_to_info(info: Info, r: Rule)
|
||||
{
|
||||
info$action = fmt("%s", r$ty);
|
||||
info$target = r$target;
|
||||
|
||||
if ( r?$location )
|
||||
info$location = r$location;
|
||||
|
||||
entity_to_info(info, r$entity);
|
||||
}
|
||||
|
||||
function log_msg(msg: string, p: PluginState)
|
||||
{
|
||||
Log::write(LOG, [$ts=network_time(), $category=MESSAGE, $msg=msg, $plugin=p$plugin$name(p)]);
|
||||
}
|
||||
|
||||
function log_error(msg: string, p: PluginState)
|
||||
{
|
||||
Log::write(LOG, [$ts=network_time(), $category=ERROR, $msg=msg, $plugin=p$plugin$name(p)]);
|
||||
}
|
||||
|
||||
function log_rule(r: Rule, cmd: string, state: InfoState, p: PluginState)
|
||||
{
|
||||
local info: Info = [$ts=network_time()];
|
||||
info$category = RULE;
|
||||
info$cmd = cmd;
|
||||
info$state = state;
|
||||
info$plugin = p$plugin$name(p);
|
||||
|
||||
rule_to_info(info, r);
|
||||
|
||||
Log::write(LOG, info);
|
||||
}
|
||||
|
||||
function log_rule_error(r: Rule, msg: string, p: PluginState)
|
||||
{
|
||||
local info: Info = [$ts=network_time(), $category=ERROR, $msg=msg, $plugin=p$plugin$name(p)];
|
||||
rule_to_info(info, r);
|
||||
Log::write(LOG, info);
|
||||
}
|
||||
|
||||
function log_rule_no_plugin(r: Rule, state: InfoState, msg: string)
|
||||
{
|
||||
local info: Info = [$ts=network_time()];
|
||||
info$category = RULE;
|
||||
info$state = state;
|
||||
info$msg = msg;
|
||||
|
||||
rule_to_info(info, r);
|
||||
|
||||
Log::write(LOG, info);
|
||||
}
|
||||
|
||||
function drop_connection(c: conn_id, t: interval, location: string &default="") : string
|
||||
{
|
||||
local e: Entity = [$ty=CONNECTION, $conn=c];
|
||||
local r: Rule = [$ty=DROP, $target=FORWARD, $entity=e, $expire=t, $location=location];
|
||||
|
||||
return add_rule(r);
|
||||
}
|
||||
|
||||
function drop_address(a: addr, t: interval, location: string &default="") : string
|
||||
{
|
||||
local e: Entity = [$ty=ADDRESS, $ip=addr_to_subnet(a)];
|
||||
local r: Rule = [$ty=DROP, $target=FORWARD, $entity=e, $expire=t, $location=location];
|
||||
|
||||
return add_rule(r);
|
||||
}
|
||||
|
||||
function whitelist_address(a: addr, t: interval, location: string &default="") : string
|
||||
{
|
||||
local e: Entity = [$ty=ADDRESS, $ip=addr_to_subnet(a)];
|
||||
local r: Rule = [$ty=WHITELIST, $priority=whitelist_priority, $target=FORWARD, $entity=e, $expire=t, $location=location];
|
||||
|
||||
return add_rule(r);
|
||||
}
|
||||
|
||||
function whitelist_subnet(s: subnet, t: interval, location: string &default="") : string
|
||||
{
|
||||
local e: Entity = [$ty=ADDRESS, $ip=s];
|
||||
local r: Rule = [$ty=WHITELIST, $priority=whitelist_priority, $target=FORWARD, $entity=e, $expire=t, $location=location];
|
||||
|
||||
return add_rule(r);
|
||||
}
|
||||
|
||||
function shunt_flow(f: flow_id, t: interval, location: string &default="") : string
|
||||
{
|
||||
local flow = NetControl::Flow(
|
||||
$src_h=addr_to_subnet(f$src_h),
|
||||
$src_p=f$src_p,
|
||||
$dst_h=addr_to_subnet(f$dst_h),
|
||||
$dst_p=f$dst_p
|
||||
);
|
||||
local e: Entity = [$ty=FLOW, $flow=flow];
|
||||
local r: Rule = [$ty=DROP, $target=MONITOR, $entity=e, $expire=t, $location=location];
|
||||
|
||||
return add_rule(r);
|
||||
}
|
||||
|
||||
function redirect_flow(f: flow_id, out_port: count, t: interval, location: string &default="") : string
|
||||
{
|
||||
local flow = NetControl::Flow(
|
||||
$src_h=addr_to_subnet(f$src_h),
|
||||
$src_p=f$src_p,
|
||||
$dst_h=addr_to_subnet(f$dst_h),
|
||||
$dst_p=f$dst_p
|
||||
);
|
||||
local e: Entity = [$ty=FLOW, $flow=flow];
|
||||
local r: Rule = [$ty=REDIRECT, $target=FORWARD, $entity=e, $expire=t, $location=location, $c=out_port];
|
||||
|
||||
return add_rule(r);
|
||||
}
|
||||
|
||||
function quarantine_host(infected: addr, dns: addr, quarantine: addr, t: interval, location: string &default="") : vector of string
|
||||
{
|
||||
local orules: vector of string = vector();
|
||||
local edrop: Entity = [$ty=FLOW, $flow=Flow($src_h=addr_to_subnet(infected))];
|
||||
local rdrop: Rule = [$ty=DROP, $target=FORWARD, $entity=edrop, $expire=t, $location=location];
|
||||
orules[|orules|] = add_rule(rdrop);
|
||||
|
||||
local todnse: Entity = [$ty=FLOW, $flow=Flow($src_h=addr_to_subnet(infected), $dst_h=addr_to_subnet(dns), $dst_p=53/udp)];
|
||||
local todnsr = Rule($ty=MODIFY, $target=FORWARD, $entity=todnse, $expire=t, $location=location, $mod=FlowMod($dst_h=quarantine), $priority=+5);
|
||||
orules[|orules|] = add_rule(todnsr);
|
||||
|
||||
local fromdnse: Entity = [$ty=FLOW, $flow=Flow($src_h=addr_to_subnet(dns), $src_p=53/udp, $dst_h=addr_to_subnet(infected))];
|
||||
local fromdnsr = Rule($ty=MODIFY, $target=FORWARD, $entity=fromdnse, $expire=t, $location=location, $mod=FlowMod($src_h=dns), $priority=+5);
|
||||
orules[|orules|] = add_rule(fromdnsr);
|
||||
|
||||
local wle: Entity = [$ty=FLOW, $flow=Flow($src_h=addr_to_subnet(infected), $dst_h=addr_to_subnet(quarantine), $dst_p=80/tcp)];
|
||||
local wlr = Rule($ty=WHITELIST, $target=FORWARD, $entity=wle, $expire=t, $location=location, $priority=+5);
|
||||
orules[|orules|] = add_rule(wlr);
|
||||
|
||||
return orules;
|
||||
}
|
||||
|
||||
|
||||
# Low-level functions that only runs on the manager (or standalone) Bro node.
|
||||
|
||||
function activate_impl(p: PluginState, priority: int)
|
||||
{
|
||||
p$_priority = priority;
|
||||
plugins[|plugins|] = p;
|
||||
sort(plugins, function(p1: PluginState, p2: PluginState) : int { return p2$_priority - p1$_priority; });
|
||||
|
||||
plugin_ids[plugin_counter] = p;
|
||||
p$_id = plugin_counter;
|
||||
++plugin_counter;
|
||||
|
||||
# perform one-timi initialization
|
||||
if ( p$plugin?$init )
|
||||
p$plugin$init(p);
|
||||
|
||||
log_msg(fmt("activated plugin with priority %d", priority), p);
|
||||
}
|
||||
|
||||
function add_rule_impl(rule: Rule) : string
|
||||
{
|
||||
rule$cid = ++rule_counter; # numeric id that can be used by plugins for their rules.
|
||||
|
||||
if ( ! rule?$id || rule$id == "" )
|
||||
rule$id = cat(rule$cid);
|
||||
|
||||
if ( ! hook NetControl::rule_policy(rule) )
|
||||
return "";
|
||||
|
||||
local accepted = F;
|
||||
local priority: int = +0;
|
||||
local r = rule;
|
||||
|
||||
for ( i in plugins )
|
||||
{
|
||||
local p = plugins[i];
|
||||
|
||||
# in this case, rule was accepted by earlier plugin and thus plugin has same
|
||||
# priority. accept, but give out new rule id.
|
||||
if ( accepted == T && p$_priority == priority )
|
||||
{
|
||||
r = copy(rule);
|
||||
r$cid = ++rule_counter;
|
||||
}
|
||||
else if ( accepted == T )
|
||||
# in this case, rule was accepted by earlier plugin and this plugin has a lower
|
||||
# priority. Abort and do not send there...
|
||||
break;
|
||||
|
||||
# set before, in case the plugins sends and regenerates the plugin record later.
|
||||
r$_plugin_id = p$_id;
|
||||
|
||||
if ( p$plugin$add_rule(p, r) )
|
||||
{
|
||||
accepted = T;
|
||||
priority = p$_priority;
|
||||
log_rule(r, "ADD", REQUESTED, p);
|
||||
}
|
||||
}
|
||||
|
||||
if ( accepted )
|
||||
return rule$id;
|
||||
|
||||
log_rule_no_plugin(r, FAILED, "not supported");
|
||||
return "";
|
||||
}
|
||||
|
||||
function remove_single_rule(id: string, cid: count) : bool
|
||||
{
|
||||
if ( [id,cid] !in rules )
|
||||
{
|
||||
Reporter::error(fmt("Rule %s -- %d does not exist in NetControl::remove_single_rule", id, cid));
|
||||
return F;
|
||||
}
|
||||
|
||||
local r = rules[id,cid];
|
||||
local p = plugin_ids[r$_plugin_id];
|
||||
|
||||
# remove the respective rules from its plugins..
|
||||
if ( ! p$plugin$remove_rule(p, r) )
|
||||
{
|
||||
log_rule_error(r, "remove failed", p);
|
||||
return F;
|
||||
}
|
||||
|
||||
log_rule(r, "REMOVE", REQUESTED, p);
|
||||
return T;
|
||||
}
|
||||
|
||||
function remove_rule_impl(id: string) : bool
|
||||
{
|
||||
if ( id !in id_to_cids )
|
||||
{
|
||||
Reporter::error(fmt("Rule %s does not exist in NetControl::remove_rule", id));
|
||||
return F;
|
||||
}
|
||||
|
||||
local cids = id_to_cids[id];
|
||||
|
||||
local success = T;
|
||||
for ( cid in cids )
|
||||
{
|
||||
if ( [id,cid] !in rules )
|
||||
{
|
||||
Reporter::error(fmt("Internal error in netcontrol::remove_rule - cid %d does not belong to rule %s", cid, id));
|
||||
delete cids[cid];
|
||||
next;
|
||||
}
|
||||
|
||||
if ( ! remove_single_rule(id, cid) )
|
||||
success = F;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
event rule_expire(r: Rule, p: PluginState)
|
||||
{
|
||||
if ( [r$id,r$cid] !in rules )
|
||||
# Removed already.
|
||||
return;
|
||||
|
||||
event rule_timeout(r, FlowInfo(), p);
|
||||
remove_single_rule(r$id, r$cid);
|
||||
}
|
||||
|
||||
event rule_added(r: Rule, p: PluginState, msg: string &default="")
|
||||
{
|
||||
log_rule(r, "ADD", SUCCEEDED, p);
|
||||
|
||||
rules[r$id,r$cid] = r;
|
||||
if ( r$id !in id_to_cids )
|
||||
id_to_cids[r$id] = set();
|
||||
|
||||
add id_to_cids[r$id][r$cid];
|
||||
|
||||
if ( r?$expire && ! p$plugin$can_expire )
|
||||
schedule r$expire { rule_expire(r, p) };
|
||||
}
|
||||
|
||||
event rule_removed(r: Rule, p: PluginState, msg: string &default="")
|
||||
{
|
||||
delete rules[r$id,r$cid];
|
||||
delete id_to_cids[r$id][r$cid];
|
||||
if ( |id_to_cids[r$id]| == 0 )
|
||||
delete id_to_cids[r$id];
|
||||
|
||||
log_rule(r, "REMOVE", SUCCEEDED, p);
|
||||
}
|
||||
|
||||
event rule_timeout(r: Rule, i: FlowInfo, p: PluginState)
|
||||
{
|
||||
delete rules[r$id,r$cid];
|
||||
delete id_to_cids[r$id][r$cid];
|
||||
if ( |id_to_cids[r$id]| == 0 )
|
||||
delete id_to_cids[r$id];
|
||||
|
||||
log_rule(r, "EXPIRE", TIMEOUT, p);
|
||||
}
|
||||
|
||||
event rule_error(r: Rule, p: PluginState, msg: string &default="")
|
||||
{
|
||||
log_rule_error(r, msg, p);
|
||||
# errors can occur during deletion. Since this probably means we wo't hear
|
||||
# from it again, let's just remove it if it exists...
|
||||
delete rules[r$id,r$cid];
|
||||
delete id_to_cids[r$id][r$cid];
|
||||
if ( |id_to_cids[r$id]| == 0 )
|
||||
delete id_to_cids[r$id];
|
||||
}
|
||||
|
||||
function clear()
|
||||
{
|
||||
for ( [id,cid] in rules )
|
||||
remove_single_rule(id, cid);
|
||||
}
|
18
scripts/base/frameworks/netcontrol/non-cluster.bro
Normal file
18
scripts/base/frameworks/netcontrol/non-cluster.bro
Normal file
|
@ -0,0 +1,18 @@
|
|||
module NetControl;
|
||||
|
||||
@load ./main
|
||||
|
||||
function activate(p: PluginState, priority: int)
|
||||
{
|
||||
activate_impl(p, priority);
|
||||
}
|
||||
|
||||
function add_rule(r: Rule) : string
|
||||
{
|
||||
return add_rule_impl(r);
|
||||
}
|
||||
|
||||
function remove_rule(id: string) : bool
|
||||
{
|
||||
return remove_rule_impl(id);
|
||||
}
|
80
scripts/base/frameworks/netcontrol/plugin.bro
Normal file
80
scripts/base/frameworks/netcontrol/plugin.bro
Normal file
|
@ -0,0 +1,80 @@
|
|||
|
||||
module NetControl;
|
||||
|
||||
@load ./types
|
||||
|
||||
export {
|
||||
## State for a plugin instance.
|
||||
type PluginState: record {
|
||||
## Table for a plugin to store custom, instance-specfific state.
|
||||
config: table[string] of string &default=table();
|
||||
|
||||
## Unique plugin identifier -- used for backlookup of plugins from Rules. Set internally.
|
||||
_id: count &optional;
|
||||
|
||||
## Set internally.
|
||||
_priority: int &default=+0;
|
||||
};
|
||||
|
||||
# Definition of a plugin.
|
||||
#
|
||||
# Generally a plugin needs to implement only what it can support. By
|
||||
# returning failure, it indicates that it can't support something and the
|
||||
# the framework will then try another plugin, if available; or informn the
|
||||
# that the operation failed. If a function isn't implemented by a plugin,
|
||||
# that's considered an implicit failure to support the operation.
|
||||
#
|
||||
# If plugin accepts a rule operation, it *must* generate one of the reporting
|
||||
# events ``rule_{added,remove,error}`` to signal if it indeed worked out;
|
||||
# this is separate from accepting the operation because often a plugin
|
||||
# will only know later (i.e., asynchrously) if that was an error for
|
||||
# something it thought it could handle.
|
||||
type Plugin: record {
|
||||
# Returns a descriptive name of the plugin instance, suitable for use in logging
|
||||
# messages. Note that this function is not optional.
|
||||
name: function(state: PluginState) : string;
|
||||
|
||||
## If true, plugin can expire rules itself. If false,
|
||||
## framework will manage rule expiration.
|
||||
can_expire: bool;
|
||||
|
||||
# One-time initialization function called when plugin gets registered, and
|
||||
# before any other methods are called.
|
||||
init: function(state: PluginState) &optional;
|
||||
|
||||
# One-time finalization function called when a plugin is shutdown; no further
|
||||
# functions will be called afterwords.
|
||||
done: function(state: PluginState) &optional;
|
||||
|
||||
# Implements the add_rule() operation. If the plugin accepts the rule,
|
||||
# it returns true, false otherwise. The rule will already have its
|
||||
# ``id`` field set, which the plugin may use for identification
|
||||
# purposes.
|
||||
add_rule: function(state: PluginState, r: Rule) : bool &optional;
|
||||
|
||||
# Implements the remove_rule() operation. This will only be called for
|
||||
# rules that the plugins has previously accepted with add_rule(). The
|
||||
# ``id`` field will match that of the add_rule() call. Generally,
|
||||
# a plugin that accepts an add_rule() should also accept the
|
||||
# remove_rule().
|
||||
remove_rule: function(state: PluginState, r: Rule) : bool &optional;
|
||||
|
||||
# A transaction groups a number of operations. The plugin can add them internally
|
||||
# and postpone putting them into effect until committed. This allows to build a
|
||||
# configuration of multiple rules at once, including replaying a previous state.
|
||||
transaction_begin: function(state: PluginState) &optional;
|
||||
transaction_end: function(state: PluginState) &optional;
|
||||
};
|
||||
|
||||
# Table for a plugin to store instance-specific configuration information.
|
||||
#
|
||||
# Note, it would be nicer to pass the Plugin instance to all the below, instead
|
||||
# of this state table. However Bro's type resolver has trouble with refering to a
|
||||
# record type from inside itself.
|
||||
redef record PluginState += {
|
||||
## The plugin that the state belongs to. (Defined separately
|
||||
## because of cyclic type dependency.)
|
||||
plugin: Plugin &optional &weaken;
|
||||
};
|
||||
|
||||
}
|
5
scripts/base/frameworks/netcontrol/plugins/__load__.bro
Normal file
5
scripts/base/frameworks/netcontrol/plugins/__load__.bro
Normal file
|
@ -0,0 +1,5 @@
|
|||
@load ./debug
|
||||
@load ./openflow
|
||||
@load ./packetfilter
|
||||
@load ./broker
|
||||
@load ./acld
|
230
scripts/base/frameworks/netcontrol/plugins/acld.bro
Normal file
230
scripts/base/frameworks/netcontrol/plugins/acld.bro
Normal file
|
@ -0,0 +1,230 @@
|
|||
# Acld plugin for the netcontrol framework.
|
||||
|
||||
module NetControl;
|
||||
|
||||
@load ../plugin
|
||||
@load base/frameworks/broker
|
||||
|
||||
export {
|
||||
type AclRule : record {
|
||||
command: string;
|
||||
cookie: count;
|
||||
arg: string;
|
||||
comment: string &optional;
|
||||
};
|
||||
|
||||
type AcldConfig: record {
|
||||
## The acld topic used to send events to
|
||||
acld_topic: string;
|
||||
## Broker host to connect to
|
||||
acld_host: addr;
|
||||
## Broker port to connect to
|
||||
acld_port: port;
|
||||
## Function that can decide weather to accept add request
|
||||
add_pred: function(p: PluginState, r: Rule, ar: AclRule): bool &optional &weaken;
|
||||
};
|
||||
|
||||
## Instantiates the acld plugin.
|
||||
global create_acld: function(config: AcldConfig) : PluginState;
|
||||
|
||||
redef record PluginState += {
|
||||
acld_config: AcldConfig &optional;
|
||||
## The ID of this acld instance - for the mapping to PluginStates
|
||||
acld_id: count &optional;
|
||||
};
|
||||
|
||||
global acld_add_rule: event(id: count, r: Rule, ar: AclRule);
|
||||
global acld_remove_rule: event(id: count, r: Rule, ar: AclRule);
|
||||
|
||||
global acld_rule_added: event(id: count, r: Rule, msg: string);
|
||||
global acld_rule_removed: event(id: count, r: Rule, msg: string);
|
||||
global acld_rule_error: event(id: count, r: Rule, msg: string);
|
||||
}
|
||||
|
||||
global netcontrol_acld_topics: set[string] = set();
|
||||
global netcontrol_acld_id: table[count] of PluginState = table();
|
||||
global netcontrol_acld_current_id: count = 0;
|
||||
|
||||
const acld_add_to_remove: table[string] of string = {
|
||||
["drop"] = "restore",
|
||||
["whitelist"] = "remwhitelist",
|
||||
["blockhosthost"] = "restorehosthost",
|
||||
["droptcpport"] = "restoretcpport",
|
||||
["dropudpport"] = "restoreudpport",
|
||||
["droptcpdsthostport"] ="restoretcpdsthostport",
|
||||
["dropudpdsthostport"] ="restoreudpdsthostport",
|
||||
["permittcpdsthostport"] ="unpermittcpdsthostport",
|
||||
["permitudpdsthostport"] ="unpermitudpdsthostport",
|
||||
["nullzero "] ="nonullzero"
|
||||
};
|
||||
|
||||
event NetControl::acld_rule_added(id: count, r: Rule, msg: string)
|
||||
{
|
||||
if ( id !in netcontrol_acld_id )
|
||||
{
|
||||
Reporter::error(fmt("NetControl acld plugin with id %d not found, aborting", id));
|
||||
return;
|
||||
}
|
||||
|
||||
local p = netcontrol_acld_id[id];
|
||||
|
||||
event NetControl::rule_added(r, p, msg);
|
||||
}
|
||||
|
||||
event NetControl::acld_rule_removed(id: count, r: Rule, msg: string)
|
||||
{
|
||||
if ( id !in netcontrol_acld_id )
|
||||
{
|
||||
Reporter::error(fmt("NetControl acld plugin with id %d not found, aborting", id));
|
||||
return;
|
||||
}
|
||||
|
||||
local p = netcontrol_acld_id[id];
|
||||
|
||||
event NetControl::rule_removed(r, p, msg);
|
||||
}
|
||||
|
||||
event NetControl::acld_rule_error(id: count, r: Rule, msg: string)
|
||||
{
|
||||
if ( id !in netcontrol_acld_id )
|
||||
{
|
||||
Reporter::error(fmt("NetControl acld plugin with id %d not found, aborting", id));
|
||||
return;
|
||||
}
|
||||
|
||||
local p = netcontrol_acld_id[id];
|
||||
|
||||
event NetControl::rule_error(r, p, msg);
|
||||
}
|
||||
|
||||
function acld_name(p: PluginState) : string
|
||||
{
|
||||
return fmt("PACF acld plugin - using broker topic %s", p$acld_config$acld_topic);
|
||||
}
|
||||
|
||||
# check that subnet specifies an addr
|
||||
function check_sn(sn: subnet) : bool
|
||||
{
|
||||
if ( is_v4_subnet(sn) && subnet_width(sn) == 32 )
|
||||
return T;
|
||||
if ( is_v6_subnet(sn) && subnet_width(sn) == 128 )
|
||||
return T;
|
||||
|
||||
Reporter::error(fmt("Acld: rule_to_acl_rule was given a subnet that does not specify a distinct address where needed - %s", sn));
|
||||
return F;
|
||||
}
|
||||
|
||||
function rule_to_acl_rule(r: Rule) : AclRule
|
||||
{
|
||||
local e = r$entity;
|
||||
|
||||
local command: string = "";
|
||||
local arg: string = "";
|
||||
|
||||
if ( e$ty == ADDRESS )
|
||||
{
|
||||
if ( r$ty == DROP )
|
||||
command = "drop";
|
||||
else if ( r$ty == WHITELIST )
|
||||
command = "whitelist";
|
||||
arg = cat(e$ip);
|
||||
}
|
||||
else if ( e$ty == FLOW )
|
||||
{
|
||||
local f = e$flow;
|
||||
if ( ( ! f?$src_h ) && ( ! f?$src_p ) && f?$dst_h && f?$dst_p && ( ! f?$src_m ) && ( ! f?$dst_m ) )
|
||||
{
|
||||
if ( !check_sn(f$dst_h) )
|
||||
command = ""; # invalid addr, do nothing
|
||||
else if ( is_tcp_port(f$dst_p) && r$ty == DROP )
|
||||
command = "droptcpdsthostport";
|
||||
else if ( is_tcp_port(f$dst_p) && r$ty == WHITELIST )
|
||||
command = "permittcpdsthostport";
|
||||
else if ( is_udp_port(f$dst_p) && r$ty == DROP)
|
||||
command = "dropucpdsthostport";
|
||||
else if ( is_udp_port(f$dst_p) && r$ty == WHITELIST)
|
||||
command = "permitucpdsthostport";
|
||||
|
||||
arg = fmt("%s %d", subnet_to_addr(f$dst_h), f$dst_p);
|
||||
}
|
||||
else if ( f?$src_h && ( ! f?$src_p ) && f?$dst_h && ( ! f?$dst_p ) && ( ! f?$src_m ) && ( ! f?$dst_m ) )
|
||||
{
|
||||
if ( !check_sn(f$src_h) || !check_sn(f$dst_h) )
|
||||
command = "";
|
||||
else if ( r$ty == DROP )
|
||||
command = "blockhosthost";
|
||||
arg = fmt("%s %s", subnet_to_addr(f$src_h), subnet_to_addr(f$dst_h));
|
||||
}
|
||||
else if ( ( ! f?$src_h ) && ( ! f?$src_p ) && ( ! f?$dst_h ) && f?$dst_p && ( ! f?$src_m ) && ( ! f?$dst_m ) )
|
||||
{
|
||||
if ( is_tcp_port(f$dst_p) && r$ty == DROP )
|
||||
command = "droptcpport";
|
||||
else if ( is_udp_port(f$dst_p) && r$ty == DROP )
|
||||
command = "dropudpport";
|
||||
arg = fmt("%d", f$dst_p);
|
||||
}
|
||||
}
|
||||
|
||||
local ar = AclRule($command=command, $cookie=r$cid, $arg=arg);
|
||||
if ( r?$location )
|
||||
ar$comment = r$location;
|
||||
return ar;
|
||||
}
|
||||
|
||||
function acld_add_rule_fun(p: PluginState, r: Rule) : bool
|
||||
{
|
||||
local ar = rule_to_acl_rule(r);
|
||||
|
||||
if ( p$acld_config?$add_pred )
|
||||
if ( ! p$acld_config$add_pred(p, r, ar) )
|
||||
return F;
|
||||
|
||||
if ( ar$command == "" )
|
||||
return F;
|
||||
|
||||
BrokerComm::event(p$acld_config$acld_topic, BrokerComm::event_args(acld_add_rule, p$acld_id, r, ar));
|
||||
return T;
|
||||
}
|
||||
|
||||
function acld_remove_rule_fun(p: PluginState, r: Rule) : bool
|
||||
{
|
||||
local ar = rule_to_acl_rule(r);
|
||||
if ( ar$command in acld_add_to_remove )
|
||||
ar$command = acld_add_to_remove[ar$command];
|
||||
else
|
||||
return F;
|
||||
|
||||
BrokerComm::event(p$acld_config$acld_topic, BrokerComm::event_args(acld_remove_rule, p$acld_id, r, ar));
|
||||
return T;
|
||||
}
|
||||
|
||||
function acld_init(p: PluginState)
|
||||
{
|
||||
BrokerComm::enable();
|
||||
BrokerComm::connect(cat(p$acld_config$acld_host), p$acld_config$acld_port, 1sec);
|
||||
BrokerComm::subscribe_to_events(p$acld_config$acld_topic);
|
||||
}
|
||||
|
||||
global acld_plugin = Plugin(
|
||||
$name=acld_name,
|
||||
$can_expire = F,
|
||||
$add_rule = acld_add_rule_fun,
|
||||
$remove_rule = acld_remove_rule_fun,
|
||||
$init = acld_init
|
||||
);
|
||||
|
||||
function create_acld(config: AcldConfig) : PluginState
|
||||
{
|
||||
if ( config$acld_topic in netcontrol_acld_topics )
|
||||
Reporter::warning(fmt("Topic %s was added to NetControl acld plugin twice. Possible duplication of commands", config$acld_topic));
|
||||
else
|
||||
add netcontrol_acld_topics[config$acld_topic];
|
||||
|
||||
local p: PluginState = [$acld_config=config, $plugin=acld_plugin, $acld_id=netcontrol_acld_current_id];
|
||||
|
||||
netcontrol_acld_id[netcontrol_acld_current_id] = p;
|
||||
++netcontrol_acld_current_id;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
147
scripts/base/frameworks/netcontrol/plugins/broker.bro
Normal file
147
scripts/base/frameworks/netcontrol/plugins/broker.bro
Normal file
|
@ -0,0 +1,147 @@
|
|||
# Broker plugin for the netcontrol framework. Sends the raw data structures
|
||||
# used in netcontrol on to Broker to allow for easy handling, e.g., of
|
||||
# command-line scripts.
|
||||
|
||||
module NetControl;
|
||||
|
||||
@load ../plugin
|
||||
@load base/frameworks/broker
|
||||
|
||||
export {
|
||||
## Instantiates the broker plugin.
|
||||
global create_broker: function(host: addr, host_port: port, topic: string, can_expire: bool &default=F) : PluginState;
|
||||
|
||||
redef record PluginState += {
|
||||
## The broker topic used to send events to
|
||||
broker_topic: string &optional;
|
||||
## The ID of this broker instance - for the mapping to PluginStates
|
||||
broker_id: count &optional;
|
||||
## Broker host to connect to
|
||||
broker_host: addr &optional;
|
||||
## Broker port to connect to
|
||||
broker_port: port &optional;
|
||||
};
|
||||
|
||||
global broker_add_rule: event(id: count, r: Rule);
|
||||
global broker_remove_rule: event(id: count, r: Rule);
|
||||
|
||||
global broker_rule_added: event(id: count, r: Rule, msg: string);
|
||||
global broker_rule_removed: event(id: count, r: Rule, msg: string);
|
||||
global broker_rule_error: event(id: count, r: Rule, msg: string);
|
||||
global broker_rule_timeout: event(id: count, r: Rule, i: FlowInfo);
|
||||
}
|
||||
|
||||
global netcontrol_broker_topics: set[string] = set();
|
||||
global netcontrol_broker_id: table[count] of PluginState = table();
|
||||
global netcontrol_broker_current_id: count = 0;
|
||||
|
||||
event NetControl::broker_rule_added(id: count, r: Rule, msg: string)
|
||||
{
|
||||
if ( id !in netcontrol_broker_id )
|
||||
{
|
||||
Reporter::error(fmt("NetControl broker plugin with id %d not found, aborting", id));
|
||||
return;
|
||||
}
|
||||
|
||||
local p = netcontrol_broker_id[id];
|
||||
|
||||
event NetControl::rule_added(r, p, msg);
|
||||
}
|
||||
|
||||
event NetControl::broker_rule_removed(id: count, r: Rule, msg: string)
|
||||
{
|
||||
if ( id !in netcontrol_broker_id )
|
||||
{
|
||||
Reporter::error(fmt("NetControl broker plugin with id %d not found, aborting", id));
|
||||
return;
|
||||
}
|
||||
|
||||
local p = netcontrol_broker_id[id];
|
||||
|
||||
event NetControl::rule_removed(r, p, msg);
|
||||
}
|
||||
|
||||
event NetControl::broker_rule_error(id: count, r: Rule, msg: string)
|
||||
{
|
||||
if ( id !in netcontrol_broker_id )
|
||||
{
|
||||
Reporter::error(fmt("NetControl broker plugin with id %d not found, aborting", id));
|
||||
return;
|
||||
}
|
||||
|
||||
local p = netcontrol_broker_id[id];
|
||||
|
||||
event NetControl::rule_error(r, p, msg);
|
||||
}
|
||||
|
||||
event NetControl::broker_rule_timeout(id: count, r: Rule, i: FlowInfo)
|
||||
{
|
||||
if ( id !in netcontrol_broker_id )
|
||||
{
|
||||
Reporter::error(fmt("NetControl broker plugin with id %d not found, aborting", id));
|
||||
return;
|
||||
}
|
||||
|
||||
local p = netcontrol_broker_id[id];
|
||||
|
||||
event NetControl::rule_timeout(r, i, p);
|
||||
}
|
||||
|
||||
function broker_name(p: PluginState) : string
|
||||
{
|
||||
return fmt("PACF Broker plugin - topic %s", p$broker_topic);
|
||||
}
|
||||
|
||||
function broker_add_rule_fun(p: PluginState, r: Rule) : bool
|
||||
{
|
||||
BrokerComm::event(p$broker_topic, BrokerComm::event_args(broker_add_rule, p$broker_id, r));
|
||||
return T;
|
||||
}
|
||||
|
||||
function broker_remove_rule_fun(p: PluginState, r: Rule) : bool
|
||||
{
|
||||
BrokerComm::event(p$broker_topic, BrokerComm::event_args(broker_remove_rule, p$broker_id, r));
|
||||
return T;
|
||||
}
|
||||
|
||||
function broker_init(p: PluginState)
|
||||
{
|
||||
BrokerComm::enable();
|
||||
BrokerComm::connect(cat(p$broker_host), p$broker_port, 1sec);
|
||||
BrokerComm::subscribe_to_events(p$broker_topic);
|
||||
}
|
||||
|
||||
global broker_plugin = Plugin(
|
||||
$name=broker_name,
|
||||
$can_expire = F,
|
||||
$add_rule = broker_add_rule_fun,
|
||||
$remove_rule = broker_remove_rule_fun,
|
||||
$init = broker_init
|
||||
);
|
||||
|
||||
global broker_plugin_can_expire = Plugin(
|
||||
$name=broker_name,
|
||||
$can_expire = T,
|
||||
$add_rule = broker_add_rule_fun,
|
||||
$remove_rule = broker_remove_rule_fun,
|
||||
$init = broker_init
|
||||
);
|
||||
|
||||
function create_broker(host: addr, host_port: port, topic: string, can_expire: bool &default=F) : PluginState
|
||||
{
|
||||
if ( topic in netcontrol_broker_topics )
|
||||
Reporter::warning(fmt("Topic %s was added to NetControl broker plugin twice. Possible duplication of commands", topic));
|
||||
else
|
||||
add netcontrol_broker_topics[topic];
|
||||
|
||||
local plugin = broker_plugin;
|
||||
if ( can_expire )
|
||||
plugin = broker_plugin_can_expire;
|
||||
|
||||
local p: PluginState = [$broker_host=host, $broker_port=host_port, $plugin=plugin, $broker_topic=topic, $broker_id=netcontrol_broker_current_id];
|
||||
|
||||
netcontrol_broker_id[netcontrol_broker_current_id] = p;
|
||||
++netcontrol_broker_current_id;
|
||||
|
||||
return p;
|
||||
}
|
95
scripts/base/frameworks/netcontrol/plugins/debug.bro
Normal file
95
scripts/base/frameworks/netcontrol/plugins/debug.bro
Normal file
|
@ -0,0 +1,95 @@
|
|||
|
||||
@load ../plugin
|
||||
|
||||
module NetControl;
|
||||
|
||||
export {
|
||||
## Instantiates a debug plugin for the PACF framework. The debug
|
||||
## plugin simply logs the operations it receives.
|
||||
##
|
||||
## do_something: If true, the plugin will claim it supports all operations; if
|
||||
## false, it will indicate it doesn't support any.
|
||||
global create_debug: function(do_something: bool) : PluginState;
|
||||
}
|
||||
|
||||
function do_something(p: PluginState) : bool
|
||||
{
|
||||
return p$config["all"] == "1";
|
||||
}
|
||||
|
||||
function debug_name(p: PluginState) : string
|
||||
{
|
||||
return fmt("Debug-%s", (do_something(p) ? "All" : "None"));
|
||||
}
|
||||
|
||||
function debug_log(p: PluginState, msg: string)
|
||||
{
|
||||
print fmt("netcontrol debug (%s): %s", debug_name(p), msg);
|
||||
}
|
||||
|
||||
function debug_init(p: PluginState)
|
||||
{
|
||||
debug_log(p, "init");
|
||||
}
|
||||
|
||||
function debug_done(p: PluginState)
|
||||
{
|
||||
debug_log(p, "init");
|
||||
}
|
||||
|
||||
function debug_add_rule(p: PluginState, r: Rule) : bool
|
||||
{
|
||||
local s = fmt("add_rule: %s", r);
|
||||
debug_log(p, s);
|
||||
|
||||
if ( do_something(p) )
|
||||
{
|
||||
event NetControl::rule_added(r, p);
|
||||
return T;
|
||||
}
|
||||
|
||||
return F;
|
||||
}
|
||||
|
||||
function debug_remove_rule(p: PluginState, r: Rule) : bool
|
||||
{
|
||||
local s = fmt("remove_rule: %s", r);
|
||||
debug_log(p, s);
|
||||
|
||||
event NetControl::rule_removed(r, p);
|
||||
return T;
|
||||
}
|
||||
|
||||
function debug_transaction_begin(p: PluginState)
|
||||
{
|
||||
debug_log(p, "transaction_begin");
|
||||
}
|
||||
|
||||
function debug_transaction_end(p: PluginState)
|
||||
{
|
||||
debug_log(p, "transaction_end");
|
||||
}
|
||||
|
||||
global debug_plugin = Plugin(
|
||||
$name=debug_name,
|
||||
$can_expire = F,
|
||||
$init = debug_init,
|
||||
$done = debug_done,
|
||||
$add_rule = debug_add_rule,
|
||||
$remove_rule = debug_remove_rule,
|
||||
$transaction_begin = debug_transaction_begin,
|
||||
$transaction_end = debug_transaction_end
|
||||
);
|
||||
|
||||
function create_debug(do_something: bool) : PluginState
|
||||
{
|
||||
local p: PluginState = [$plugin=debug_plugin];
|
||||
|
||||
# FIXME: Why's the default not working?
|
||||
p$config = table();
|
||||
p$config["all"] = (do_something ? "1" : "0");
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
|
402
scripts/base/frameworks/netcontrol/plugins/openflow.bro
Normal file
402
scripts/base/frameworks/netcontrol/plugins/openflow.bro
Normal file
|
@ -0,0 +1,402 @@
|
|||
@load ../plugin
|
||||
@load base/frameworks/openflow
|
||||
|
||||
module NetControl;
|
||||
|
||||
export {
|
||||
type OfConfig: record {
|
||||
monitor: bool &default=T;
|
||||
forward: bool &default=T;
|
||||
idle_timeout: count &default=0;
|
||||
table_id: count &optional;
|
||||
priority_offset: int &default=+0; ##< add this to all rule priorities. Can be useful if you want the openflow priorities be offset from the netcontrol priorities without having to write a filter function.
|
||||
|
||||
check_pred: function(p: PluginState, r: Rule): bool &optional &weaken;
|
||||
match_pred: function(p: PluginState, e: Entity, m: vector of OpenFlow::ofp_match): vector of OpenFlow::ofp_match &optional &weaken;
|
||||
flow_mod_pred: function(p: PluginState, r: Rule, m: OpenFlow::ofp_flow_mod): OpenFlow::ofp_flow_mod &optional &weaken;
|
||||
};
|
||||
|
||||
redef record PluginState += {
|
||||
## OpenFlow controller for NetControl OpenFlow plugin
|
||||
of_controller: OpenFlow::Controller &optional;
|
||||
## OpenFlow configuration record that is passed on initialization
|
||||
of_config: OfConfig &optional;
|
||||
};
|
||||
|
||||
type OfTable: record {
|
||||
p: PluginState;
|
||||
r: Rule;
|
||||
c: count &default=0; # how many replies did we see so far? needed for ids where we have multiple rules...
|
||||
packet_count: count &default=0;
|
||||
byte_count: count &default=0;
|
||||
duration_sec: double &default=0.0;
|
||||
};
|
||||
|
||||
## the time interval after which an openflow message is considered to be timed out
|
||||
## and we delete it from our internal tracking.
|
||||
const openflow_message_timeout = 20secs &redef;
|
||||
|
||||
## the time interval after we consider a flow timed out. This should be fairly high (or
|
||||
## even disabled) if you expect a lot of long flows. However, one also will have state
|
||||
## buildup for quite a while if keeping this around...
|
||||
const openflow_flow_timeout = 24hrs &redef;
|
||||
|
||||
## Instantiates an openflow plugin for the PACF framework.
|
||||
global create_openflow: function(controller: OpenFlow::Controller, config: OfConfig &default=[]) : PluginState;
|
||||
}
|
||||
|
||||
global of_messages: table[count, OpenFlow::ofp_flow_mod_command] of OfTable &create_expire=openflow_message_timeout
|
||||
&expire_func=function(t: table[count, OpenFlow::ofp_flow_mod_command] of OfTable, idx: any): interval
|
||||
{
|
||||
local rid: count;
|
||||
local command: OpenFlow::ofp_flow_mod_command;
|
||||
[rid, command] = idx;
|
||||
|
||||
local p = t[rid, command]$p;
|
||||
local r = t[rid, command]$r;
|
||||
event NetControl::rule_error(r, p, "Timeout during rule insertion/removal");
|
||||
return 0secs;
|
||||
};
|
||||
|
||||
global of_flows: table[count] of OfTable &create_expire=openflow_flow_timeout;
|
||||
|
||||
function openflow_name(p: PluginState) : string
|
||||
{
|
||||
return fmt("Openflow - %s", p$of_controller$describe(p$of_controller$state));
|
||||
}
|
||||
|
||||
function openflow_check_rule(p: PluginState, r: Rule) : bool
|
||||
{
|
||||
local c = p$of_config;
|
||||
|
||||
if ( p$of_config?$check_pred )
|
||||
return p$of_config$check_pred(p, r);
|
||||
|
||||
if ( r$target == MONITOR && c$monitor )
|
||||
return T;
|
||||
|
||||
if ( r$target == FORWARD && c$forward )
|
||||
return T;
|
||||
|
||||
return F;
|
||||
}
|
||||
|
||||
function openflow_match_pred(p: PluginState, e: Entity, m: vector of OpenFlow::ofp_match) : vector of OpenFlow::ofp_match
|
||||
{
|
||||
if ( p$of_config?$match_pred )
|
||||
return p$of_config$match_pred(p, e, m);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
function openflow_flow_mod_pred(p: PluginState, r: Rule, m: OpenFlow::ofp_flow_mod): OpenFlow::ofp_flow_mod
|
||||
{
|
||||
if ( p$of_config?$flow_mod_pred )
|
||||
return p$of_config$flow_mod_pred(p, r, m);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
function determine_dl_type(s: subnet): count
|
||||
{
|
||||
local pdl = OpenFlow::ETH_IPv4;
|
||||
if ( is_v6_subnet(s) )
|
||||
pdl = OpenFlow::ETH_IPv6;
|
||||
|
||||
return pdl;
|
||||
}
|
||||
|
||||
function determine_proto(p: port): count
|
||||
{
|
||||
local proto = OpenFlow::IP_TCP;
|
||||
if ( is_udp_port(p) )
|
||||
proto = OpenFlow::IP_UDP;
|
||||
else if ( is_icmp_port(p) )
|
||||
proto = OpenFlow::IP_ICMP;
|
||||
|
||||
return proto;
|
||||
}
|
||||
|
||||
function entity_to_match(p: PluginState, e: Entity): vector of OpenFlow::ofp_match
|
||||
{
|
||||
local v : vector of OpenFlow::ofp_match = vector();
|
||||
|
||||
if ( e$ty == CONNECTION )
|
||||
{
|
||||
v[|v|] = OpenFlow::match_conn(e$conn); # forward and...
|
||||
v[|v|] = OpenFlow::match_conn(e$conn, T); # reverse
|
||||
return openflow_match_pred(p, e, v);
|
||||
}
|
||||
|
||||
if ( e$ty == MAC )
|
||||
{
|
||||
v[|v|] = OpenFlow::ofp_match(
|
||||
$dl_src=e$mac
|
||||
);
|
||||
v[|v|] = OpenFlow::ofp_match(
|
||||
$dl_dst=e$mac
|
||||
);
|
||||
|
||||
return openflow_match_pred(p, e, v);
|
||||
}
|
||||
|
||||
local dl_type = OpenFlow::ETH_IPv4;
|
||||
|
||||
if ( e$ty == ADDRESS )
|
||||
{
|
||||
if ( is_v6_subnet(e$ip) )
|
||||
dl_type = OpenFlow::ETH_IPv6;
|
||||
|
||||
v[|v|] = OpenFlow::ofp_match(
|
||||
$dl_type=dl_type,
|
||||
$nw_src=e$ip
|
||||
);
|
||||
|
||||
v[|v|] = OpenFlow::ofp_match(
|
||||
$dl_type=dl_type,
|
||||
$nw_dst=e$ip
|
||||
);
|
||||
|
||||
return openflow_match_pred(p, e, v);
|
||||
}
|
||||
|
||||
local proto = OpenFlow::IP_TCP;
|
||||
|
||||
if ( e$ty == FLOW )
|
||||
{
|
||||
local m = OpenFlow::ofp_match();
|
||||
local f = e$flow;
|
||||
|
||||
if ( f?$src_m )
|
||||
m$dl_src=f$src_m;
|
||||
if ( f?$dst_m )
|
||||
m$dl_dst=f$dst_m;
|
||||
|
||||
if ( f?$src_h )
|
||||
{
|
||||
m$dl_type = determine_dl_type(f$src_h);
|
||||
m$nw_src = f$src_h;
|
||||
}
|
||||
|
||||
if ( f?$dst_h )
|
||||
{
|
||||
m$dl_type = determine_dl_type(f$dst_h);
|
||||
m$nw_dst = f$dst_h;
|
||||
}
|
||||
|
||||
if ( f?$src_p )
|
||||
{
|
||||
m$nw_proto = determine_proto(f$src_p);
|
||||
m$tp_src = port_to_count(f$src_p);
|
||||
}
|
||||
|
||||
if ( f?$dst_p )
|
||||
{
|
||||
m$nw_proto = determine_proto(f$dst_p);
|
||||
m$tp_dst = port_to_count(f$dst_p);
|
||||
}
|
||||
|
||||
v[|v|] = m;
|
||||
|
||||
return openflow_match_pred(p, e, v);
|
||||
}
|
||||
|
||||
Reporter::error(fmt("Entity type %s not supported for openflow yet", cat(e$ty)));
|
||||
return openflow_match_pred(p, e, v);
|
||||
}
|
||||
|
||||
function openflow_rule_to_flow_mod(p: PluginState, r: Rule) : OpenFlow::ofp_flow_mod
|
||||
{
|
||||
local c = p$of_config;
|
||||
|
||||
local flow_mod = OpenFlow::ofp_flow_mod(
|
||||
$cookie=OpenFlow::generate_cookie(r$cid*2), # leave one space for the cases in which we need two rules.
|
||||
$command=OpenFlow::OFPFC_ADD,
|
||||
$idle_timeout=c$idle_timeout,
|
||||
$priority=int_to_count(r$priority + c$priority_offset),
|
||||
$flags=OpenFlow::OFPFF_SEND_FLOW_REM # please notify us when flows are removed
|
||||
);
|
||||
|
||||
if ( r?$expire )
|
||||
flow_mod$hard_timeout = double_to_count(interval_to_double(r$expire));
|
||||
if ( c?$table_id )
|
||||
flow_mod$table_id = c$table_id;
|
||||
|
||||
if ( r$ty == DROP )
|
||||
{
|
||||
# default, nothing to do. We simply do not add an output port to the rule...
|
||||
}
|
||||
else if ( r$ty == WHITELIST )
|
||||
{
|
||||
# at the moment our interpretation of whitelist is to hand this off to the switches L2/L3 routing.
|
||||
flow_mod$actions$out_ports = vector(OpenFlow::OFPP_NORMAL);
|
||||
}
|
||||
else if ( r$ty == MODIFY )
|
||||
{
|
||||
# if no ports are given, just assume normal pipeline...
|
||||
flow_mod$actions$out_ports = vector(OpenFlow::OFPP_NORMAL);
|
||||
|
||||
local mod = r$mod;
|
||||
if ( mod?$redirect_port )
|
||||
flow_mod$actions$out_ports = vector(mod$redirect_port);
|
||||
|
||||
if ( mod?$src_h )
|
||||
flow_mod$actions$nw_src = mod$src_h;
|
||||
if ( mod?$dst_h )
|
||||
flow_mod$actions$nw_dst = mod$dst_h;
|
||||
if ( mod?$src_m )
|
||||
flow_mod$actions$dl_src = mod$src_m;
|
||||
if ( mod?$dst_m )
|
||||
flow_mod$actions$dl_dst = mod$dst_m;
|
||||
if ( mod?$src_p )
|
||||
flow_mod$actions$tp_src = mod$src_p;
|
||||
if ( mod?$dst_p )
|
||||
flow_mod$actions$tp_dst = mod$dst_p;
|
||||
}
|
||||
else if ( r$ty == REDIRECT )
|
||||
{
|
||||
# redirect to port c
|
||||
flow_mod$actions$out_ports = vector(r$c);
|
||||
}
|
||||
else
|
||||
{
|
||||
Reporter::error(fmt("Rule type %s not supported for openflow yet", cat(r$ty)));
|
||||
}
|
||||
|
||||
return openflow_flow_mod_pred(p, r, flow_mod);
|
||||
}
|
||||
|
||||
function openflow_add_rule(p: PluginState, r: Rule) : bool
|
||||
{
|
||||
if ( ! openflow_check_rule(p, r) )
|
||||
return F;
|
||||
|
||||
local flow_mod = openflow_rule_to_flow_mod(p, r);
|
||||
local matches = entity_to_match(p, r$entity);
|
||||
|
||||
for ( i in matches )
|
||||
{
|
||||
if ( OpenFlow::flow_mod(p$of_controller, matches[i], flow_mod) )
|
||||
{
|
||||
of_messages[r$cid, flow_mod$command] = OfTable($p=p, $r=r);
|
||||
flow_mod = copy(flow_mod);
|
||||
++flow_mod$cookie;
|
||||
}
|
||||
else
|
||||
event rule_error(r, p, "Error while executing OpenFlow::flow_mod");
|
||||
}
|
||||
|
||||
return T;
|
||||
}
|
||||
|
||||
function openflow_remove_rule(p: PluginState, r: Rule) : bool
|
||||
{
|
||||
if ( ! openflow_check_rule(p, r) )
|
||||
return F;
|
||||
|
||||
local flow_mod: OpenFlow::ofp_flow_mod = [
|
||||
$cookie=OpenFlow::generate_cookie(r$cid),
|
||||
$command=OpenFlow::OFPFC_DELETE
|
||||
];
|
||||
|
||||
if ( OpenFlow::flow_mod(p$of_controller, [], flow_mod) )
|
||||
of_messages[r$cid, flow_mod$command] = OfTable($p=p, $r=r);
|
||||
else
|
||||
{
|
||||
event rule_error(r, p, "Error while executing OpenFlow::flow_mod");
|
||||
return F;
|
||||
}
|
||||
|
||||
# if this was an address or mac match, we also need to remove the reverse
|
||||
if ( r$entity$ty == ADDRESS || r$entity$ty == MAC )
|
||||
{
|
||||
local flow_mod_2 = copy(flow_mod);
|
||||
++flow_mod_2$cookie;
|
||||
OpenFlow::flow_mod(p$of_controller, [], flow_mod_2);
|
||||
}
|
||||
|
||||
return T;
|
||||
}
|
||||
|
||||
event OpenFlow::flow_mod_success(match: OpenFlow::ofp_match, flow_mod: OpenFlow::ofp_flow_mod, msg: string) &priority=3
|
||||
{
|
||||
local id = OpenFlow::get_cookie_uid(flow_mod$cookie)/2;
|
||||
if ( [id, flow_mod$command] !in of_messages )
|
||||
return;
|
||||
|
||||
local r = of_messages[id,flow_mod$command]$r;
|
||||
local p = of_messages[id,flow_mod$command]$p;
|
||||
local c = of_messages[id,flow_mod$command]$c;
|
||||
|
||||
if ( r$entity$ty == ADDRESS || r$entity$ty == MAC )
|
||||
{
|
||||
++of_messages[id,flow_mod$command]$c;
|
||||
if ( of_messages[id,flow_mod$command]$c < 2 )
|
||||
return; # will do stuff once the second part arrives...
|
||||
}
|
||||
|
||||
delete of_messages[id,flow_mod$command];
|
||||
|
||||
if ( p$of_controller$supports_flow_removed )
|
||||
of_flows[id] = OfTable($p=p, $r=r);
|
||||
|
||||
if ( flow_mod$command == OpenFlow::OFPFC_ADD )
|
||||
event NetControl::rule_added(r, p, msg);
|
||||
else if ( flow_mod$command == OpenFlow::OFPFC_DELETE || flow_mod$command == OpenFlow::OFPFC_DELETE_STRICT )
|
||||
event NetControl::rule_removed(r, p, msg);
|
||||
}
|
||||
|
||||
event OpenFlow::flow_mod_failure(match: OpenFlow::ofp_match, flow_mod: OpenFlow::ofp_flow_mod, msg: string) &priority=3
|
||||
{
|
||||
local id = OpenFlow::get_cookie_uid(flow_mod$cookie)/2;
|
||||
if ( [id, flow_mod$command] !in of_messages )
|
||||
return;
|
||||
|
||||
local r = of_messages[id,flow_mod$command]$r;
|
||||
local p = of_messages[id,flow_mod$command]$p;
|
||||
delete of_messages[id,flow_mod$command];
|
||||
|
||||
event NetControl::rule_error(r, p, msg);
|
||||
}
|
||||
|
||||
event OpenFlow::flow_removed(match: OpenFlow::ofp_match, cookie: count, priority: count, reason: count, duration_sec: count, idle_timeout: count, packet_count: count, byte_count: count)
|
||||
{
|
||||
local id = OpenFlow::get_cookie_uid(cookie)/2;
|
||||
if ( id !in of_flows )
|
||||
return;
|
||||
|
||||
local rec = of_flows[id];
|
||||
local r = rec$r;
|
||||
local p = rec$p;
|
||||
|
||||
if ( r$entity$ty == ADDRESS || r$entity$ty == MAC )
|
||||
{
|
||||
++of_flows[id]$c;
|
||||
if ( of_flows[id]$c < 2 )
|
||||
return; # will do stuff once the second part arrives...
|
||||
else
|
||||
event NetControl::rule_timeout(r, FlowInfo($duration=double_to_interval((rec$duration_sec+duration_sec)/2), $packet_count=packet_count+rec$packet_count, $byte_count=byte_count+rec$byte_count), p);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
event NetControl::rule_timeout(r, FlowInfo($duration=double_to_interval(duration_sec+0.0), $packet_count=packet_count, $byte_count=byte_count), p);
|
||||
}
|
||||
|
||||
global openflow_plugin = Plugin(
|
||||
$name=openflow_name,
|
||||
$can_expire = T,
|
||||
# $init = openflow_init,
|
||||
# $done = openflow_done,
|
||||
$add_rule = openflow_add_rule,
|
||||
$remove_rule = openflow_remove_rule
|
||||
# $transaction_begin = openflow_transaction_begin,
|
||||
# $transaction_end = openflow_transaction_end
|
||||
);
|
||||
|
||||
function create_openflow(controller: OpenFlow::Controller, config: OfConfig &default=[]) : PluginState
|
||||
{
|
||||
local p: PluginState = [$plugin=openflow_plugin, $of_controller=controller, $of_config=config];
|
||||
|
||||
return p;
|
||||
}
|
113
scripts/base/frameworks/netcontrol/plugins/packetfilter.bro
Normal file
113
scripts/base/frameworks/netcontrol/plugins/packetfilter.bro
Normal file
|
@ -0,0 +1,113 @@
|
|||
# PACF plugin for the PacketFilter handling that comes with
|
||||
# Bro. Since the PacketFilter in Bro is quite limited in scope
|
||||
# and can only add/remove filters for addresses, this is quite
|
||||
# limited in scope at the moment.
|
||||
|
||||
module NetControl;
|
||||
|
||||
@load ../plugin
|
||||
|
||||
export {
|
||||
## Instantiates the packetfilter plugin.
|
||||
global create_packetfilter: function() : PluginState;
|
||||
}
|
||||
|
||||
# Check if we can handle this rule. If it specifies ports or
|
||||
# anything Bro cannot handle, simply ignore it for now.
|
||||
function packetfilter_check_rule(r: Rule) : bool
|
||||
{
|
||||
if ( r$ty != DROP )
|
||||
return F;
|
||||
|
||||
if ( r$target != MONITOR )
|
||||
return F;
|
||||
|
||||
local e = r$entity;
|
||||
if ( e$ty == ADDRESS )
|
||||
return T;
|
||||
|
||||
if ( e$ty != FLOW ) # everything else requires ports or MAC stuff
|
||||
return F;
|
||||
|
||||
if ( e$flow?$src_p || e$flow?$dst_p || e$flow?$src_m || e$flow?$dst_m )
|
||||
return F;
|
||||
|
||||
return T;
|
||||
}
|
||||
|
||||
|
||||
function packetfilter_add_rule(p: PluginState, r: Rule) : bool
|
||||
{
|
||||
if ( ! packetfilter_check_rule(r) )
|
||||
return F;
|
||||
|
||||
local e = r$entity;
|
||||
if ( e$ty == ADDRESS )
|
||||
{
|
||||
install_src_net_filter(e$ip, 0, 1.0);
|
||||
install_dst_net_filter(e$ip, 0, 1.0);
|
||||
return T;
|
||||
}
|
||||
|
||||
if ( e$ty == FLOW )
|
||||
{
|
||||
local f = e$flow;
|
||||
if ( f?$src_h )
|
||||
install_src_net_filter(f$src_h, 0, 1.0);
|
||||
if ( f?$dst_h )
|
||||
install_dst_net_filter(f$dst_h, 0, 1.0);
|
||||
|
||||
return T;
|
||||
}
|
||||
|
||||
return F;
|
||||
}
|
||||
|
||||
function packetfilter_remove_rule(p: PluginState, r: Rule) : bool
|
||||
{
|
||||
if ( ! packetfilter_check_rule(r) )
|
||||
return F;
|
||||
|
||||
local e = r$entity;
|
||||
if ( e$ty == ADDRESS )
|
||||
{
|
||||
uninstall_src_net_filter(e$ip);
|
||||
uninstall_dst_net_filter(e$ip);
|
||||
return T;
|
||||
}
|
||||
|
||||
if ( e$ty == FLOW )
|
||||
{
|
||||
local f = e$flow;
|
||||
if ( f?$src_h )
|
||||
uninstall_src_net_filter(f$src_h);
|
||||
if ( f?$dst_h )
|
||||
uninstall_dst_net_filter(f$dst_h);
|
||||
|
||||
return T;
|
||||
}
|
||||
|
||||
return F;
|
||||
}
|
||||
|
||||
function packetfilter_name(p: PluginState) : string
|
||||
{
|
||||
return "PACF plugin for the Bro packetfilter";
|
||||
}
|
||||
|
||||
global packetfilter_plugin = Plugin(
|
||||
$name=packetfilter_name,
|
||||
$can_expire = F,
|
||||
# $init = packetfilter_init,
|
||||
# $done = packetfilter_done,
|
||||
$add_rule = packetfilter_add_rule,
|
||||
$remove_rule = packetfilter_remove_rule
|
||||
);
|
||||
|
||||
function create_packetfilter() : PluginState
|
||||
{
|
||||
local p: PluginState = [$plugin=packetfilter_plugin];
|
||||
|
||||
return p;
|
||||
}
|
||||
|
111
scripts/base/frameworks/netcontrol/types.bro
Normal file
111
scripts/base/frameworks/netcontrol/types.bro
Normal file
|
@ -0,0 +1,111 @@
|
|||
|
||||
module NetControl;
|
||||
|
||||
export {
|
||||
const default_priority: int = +0 &redef;
|
||||
const whitelist_priority: int = +5 &redef;
|
||||
|
||||
## Type of a :bro:id:`Entity` for defining an action.
|
||||
type EntityType: enum {
|
||||
ADDRESS, ##< Activity involving a specific IP address.
|
||||
CONNECTION, ##< All of a bi-directional connection's activity.
|
||||
FLOW, ##< All of a uni-directional flow's activity. Can contain wildcards.
|
||||
MAC, ##< Activity involving a MAC address.
|
||||
};
|
||||
|
||||
## Type of a :bro:id:`Flow` for defining a flow.
|
||||
type Flow: record {
|
||||
src_h: subnet &optional; ##< The source IP address/subnet.
|
||||
src_p: port &optional; ##< The source port number.
|
||||
dst_h: subnet &optional; ##< The destination IP address/subnet.
|
||||
dst_p: port &optional; ##< The desintation port number.
|
||||
src_m: string &optional; ##< The source MAC address.
|
||||
dst_m: string &optional; ##< The destination MAC address.
|
||||
};
|
||||
|
||||
## Type defining the enity an :bro:id:`Rule` is operating on.
|
||||
type Entity: record {
|
||||
ty: EntityType; ##< Type of entity.
|
||||
conn: conn_id &optional; ##< Used with :bro:id:`CONNECTION` .
|
||||
flow: Flow &optional; ##< Used with :bro:id:`FLOW` .
|
||||
ip: subnet &optional; ##< Used with bro:id:`ADDRESS`; can specifiy a CIDR subnet.
|
||||
mac: string &optional; ##< Used with :bro:id:`MAC`.
|
||||
};
|
||||
|
||||
## Target of :bro:id:`Rule` action.
|
||||
type TargetType: enum {
|
||||
FORWARD, #< Apply rule actively to traffic on forwarding path.
|
||||
MONITOR, #< Apply rule passively to traffic sent to Bro for monitoring.
|
||||
};
|
||||
|
||||
## Type of rules that the framework supports. Each type lists the
|
||||
## :bro:id:`Rule` argument(s) it uses, if any.
|
||||
##
|
||||
## Plugins may extend this type to define their own.
|
||||
type RuleType: enum {
|
||||
## Stop forwarding all packets matching entity.
|
||||
##
|
||||
## No arguments.
|
||||
DROP,
|
||||
|
||||
## Begin modifying all packets matching entity.
|
||||
##
|
||||
## .. todo::
|
||||
## Define arguments.
|
||||
MODIFY,
|
||||
|
||||
## Begin redirecting all packets matching entity.
|
||||
##
|
||||
## .. todo::
|
||||
## c: output port to redirect traffic to.
|
||||
REDIRECT,
|
||||
|
||||
## Whitelists all packets of an entity, meaning no restrictions will be applied.
|
||||
## While whitelisting is the default if no rule matches an this can type can be
|
||||
## used to override lower-priority rules that would otherwise take effect for the
|
||||
## entity.
|
||||
WHITELIST,
|
||||
};
|
||||
|
||||
## Type of a :bro:id:`FlowMod` for defining a flow modification action.
|
||||
type FlowMod: record {
|
||||
src_h: addr &optional; ##< The source IP address.
|
||||
src_p: count &optional; ##< The source port number.
|
||||
dst_h: addr &optional; ##< The destination IP address.
|
||||
dst_p: count &optional; ##< The desintation port number.
|
||||
src_m: string &optional; ##< The source MAC address.
|
||||
dst_m: string &optional; ##< The destination MAC address.
|
||||
redirect_port: count &optional;
|
||||
};
|
||||
|
||||
## A rule for the framework to put in place. Of all rules currently in
|
||||
## place, the first match will be taken, sorted by priority. All
|
||||
## further rules will be ignored.
|
||||
type Rule: record {
|
||||
ty: RuleType; ##< Type of rule.
|
||||
target: TargetType; ##< Where to apply rule.
|
||||
entity: Entity; ##< Entity to apply rule to.
|
||||
expire: interval &optional; ##< Timeout after which to expire the rule.
|
||||
priority: int &default=default_priority; ##< Priority if multiple rules match an entity (larger value is higher priority).
|
||||
location: string &optional; ##< Optional string describing where/what installed the rule.
|
||||
|
||||
c: count &optional; ##< Argument for rule types requiring an count argument.
|
||||
i: int &optional; ##< Argument for rule types requiring an integer argument.
|
||||
d: double &optional; ##< Argument for rule types requiring a double argument.
|
||||
s: string &optional; ##< Argument for rule types requiring a string argument.
|
||||
mod: FlowMod &optional; ##< Argument for :bro:id:`MODIFY` rules.
|
||||
|
||||
id: string &default=""; ##< Internally determined unique ID for this rule. Will be set when added.
|
||||
cid: count &default=0; ##< Internally determined unique numeric ID for this rule. Set when added.
|
||||
};
|
||||
|
||||
## Information of a flow that can be provided by switches when the flow times out.
|
||||
## Currently this is heavily influenced by the data that OpenFlow returns by default.
|
||||
## That being said - their design makes sense and this is probably the data one
|
||||
## can expect to be available.
|
||||
type FlowInfo: record {
|
||||
duration: interval &optional; ##< total duration of the rule
|
||||
packet_count: count &optional; ##< number of packets exchanged over connections matched by the rule
|
||||
byte_count: count &optional; ##< total bytes exchanged over connections matched by the rule
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue