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

Great work, and great documentation!

I'm getting one test failure with
scripts.base.frameworks.netcontrol.catch-and-release-cluster Going
ahead and commiting, Jenkins will show the details I assume.

BIT-1584 #merged

* origin/topic/johanna/netcontrol-improvements:
  SMTP does not need to pull in the notice framework.
  Write NetControl framework documentation.
  Use NetControl for ACTION_DROP of notice framework.
  NetControl: slightly update catch and release logging
  NetControl: fix several small logging issues
  NetControl: more catch and release logging and cluster fix
  NetControl: rewrite catch and release and small fixes.
  NetControl: find_rules_subnet works in cluster mode
  NetControl: fix acld whitelist command
  NetControl: add rule exists as state besides added and failure.
  NetControl: Suppress duplicate "plugin activated" messages.
  NetControl: make new broker plugin options accessible
  NetControl: add predicates to broker plugin
This commit is contained in:
Robin Sommer 2016-06-30 17:27:54 -07:00
commit 09ea84bb6e
115 changed files with 3072 additions and 301 deletions

View file

@ -0,0 +1,3 @@
The NetControl framework provides a way for Bro to interact with networking
hard- and software, e.g. for dropping and shunting IP addresses/connections,
etc.

View file

@ -2,103 +2,508 @@
module NetControl;
@load base/frameworks/cluster
@load ./main
@load ./drop
export {
redef enum Log::ID += { CATCH_RELEASE };
## Thhis record is used is used for storing information about current blocks that are
## part of catch and release.
type BlockInfo: record {
## Absolute time indicating until when a block is inserted using NetControl
block_until: time &optional;
## Absolute time indicating until when an IP address is watched to reblock it
watch_until: time;
## Number of times an IP address was reblocked
num_reblocked: count &default=0;
## Number indicating at which catch and release interval we currently are
current_interval: count;
## ID of the inserted block, if any.
current_block_id: string;
## User specified string
location: string &optional;
};
## The enum that contains the different kinds of messages that are logged by
## catch and release
type CatchReleaseActions: enum {
## Log lines marked with info are purely informational; no action was taken
INFO,
## A rule for the specified IP address already existed in NetControl (outside
## of catch-and-release). Catch and release did not add a new rule, but is now
## watching the IP address and will add a new rule after the current rule expired.
ADDED,
## A drop was requested by catch and release
DROP,
## A address was succesfully blocked by catch and release
DROPPED,
## An address was unblocked after the timeout expired
UNBLOCK,
## An address was forgotten because it did not reappear within the `watch_until` interval
FORGOTTEN,
## A watched IP address was seen again; catch and release will re-block it.
SEEN_AGAIN
};
## The record type that is used for representing and logging
type CatchReleaseInfo: record {
## The absolute time indicating when the action for this log-line occured.
ts: time &log;
## The rule id that this log lone refers to.
rule_id: string &log &optional;
## The IP address that this line refers to.
ip: addr &log;
## The action that was taken in this log-line.
action: CatchReleaseActions &log;
## The current block_interaval (for how long the address is blocked).
block_interval: interval &log &optional;
## The current watch_interval (for how long the address will be watched and re-block if it reappears).
watch_interval: interval &log &optional;
## The absolute time until which the address is blocked.
blocked_until: time &log &optional;
## The absolute time until which the address will be monitored.
watched_until: time &log &optional;
## Number of times that this address was blocked in the current cycle.
num_blocked: count &log &optional;
## The user specified location string.
location: string &log &optional;
## Additional informational string by the catch and release framework about this log-line.
message: string &log &optional;
};
## 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.
##
## In cluster mode, this function works on workers as well as the manager. On managers,
## the returned :bro:see:`NetControl::BlockInfo` record will not contain the block ID,
## which will be assigned on the manager.
##
## 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;
## Returns: The :bro:see:`NetControl::BlockInfo` record containing information about
## the inserted block.
global drop_address_catch_release: function(a: addr, location: string &default="") : BlockInfo;
## Time intervals for which a subsequent drops of the same IP take
## effect.
## Removes an address from being watched with catch and release. Returns true if the
## address was found and removed; returns false if it was unknown to catch and release.
##
## If the address is currently blocked, and the block was inserted by catch and release,
## the block is removed.
##
## a: The address to be unblocked.
##
## Returns: True if the address was unblocked.
global unblock_address_catch_release: function(a: addr) : bool;
## This function can be called to notify the cach and release script that activity by
## an IP address was seen. If the respective IP address is currently monitored by catch and
## release and not blocked, the block will be re-instated. See the documentation of watch_new_connection
## which events the catch and release functionality usually monitors for activity.
##
## a: The address that was seen and should be re-dropped if it is being watched
global catch_release_seen: function(a: addr);
## Get the :bro:see:`NetControl::BlockInfo` record for an address currently blocked by catch and release.
## If the address is unknown to catch and release, the watch_until time will be set to 0.
##
## In cluster mode, this function works on the manager and workers. On workers, the data will
## lag slightly behind the manager; if you add a block, it will not be instantly available via
## this function.
##
## a: The address to get information about.
##
## Returns: The :bro:see:`NetControl::BlockInfo` record containing information about
## the inserted block.
global get_catch_release_info: function(a: addr) : BlockInfo;
## If true, catch_release_seen is called on the connection originator in new_connection,
## connection_established, partial_connection, connection_attempt, connection_rejected,
## connection_reset and connection_pending
const watch_connections = T &redef;
## If true, catch and release warns if packets of an IP address are still seen after it
## should have been blocked.
const catch_release_warn_blocked_ip_encountered = F &redef;
## Time intervals for which a subsequent drops of the same IP take
## effect.
const catch_release_intervals: vector of interval = vector(10min, 1hr, 24hrs, 7days) &redef;
## Event that can be handled to access the :bro:type:`NetControl::CatchReleaseInfo`
## record as it is sent on to the logging framework.
global log_netcontrol_catch_release: event(rec: CatchReleaseInfo);
# Cluster events for catch and release
global catch_release_block_new: event(a: addr, b: BlockInfo);
global catch_release_block_delete: event(a: addr);
global catch_release_add: event(a: addr, location: string);
global catch_release_delete: event(a: addr);
global catch_release_encountered: event(a: addr);
}
function per_block_interval(t: table[addr] of count, idx: addr): interval
# set that is used to only send seen notifications to the master every ~30 seconds.
global catch_release_recently_notified: set[addr] &create_expire=30secs;
event bro_init() &priority=5
{
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;
Log::create_stream(NetControl::CATCH_RELEASE, [$columns=CatchReleaseInfo, $ev=log_netcontrol_catch_release, $path="netcontrol_catch_release"]);
}
# This is the internally maintained table containing all the currently going on catch-and-release
# blocks.
global blocks: table[addr] of count = {}
function get_watch_interval(current_interval: count): interval
{
if ( (current_interval + 1) in catch_release_intervals )
return catch_release_intervals[current_interval+1];
else
return catch_release_intervals[current_interval];
}
function populate_log_record(ip: addr, bi: BlockInfo, action: CatchReleaseActions): CatchReleaseInfo
{
local log = CatchReleaseInfo($ts=network_time(), $ip=ip, $action=action,
$block_interval=catch_release_intervals[bi$current_interval],
$watch_interval=get_watch_interval(bi$current_interval),
$watched_until=bi$watch_until,
$num_blocked=bi$num_reblocked+1
);
if ( bi?$block_until )
log$blocked_until = bi$block_until;
if ( bi?$current_block_id && bi$current_block_id != "" )
log$rule_id = bi$current_block_id;
if ( bi?$location )
log$location = bi$location;
return log;
}
function per_block_interval(t: table[addr] of BlockInfo, idx: addr): interval
{
local remaining_time = t[idx]$watch_until - network_time();
if ( remaining_time < 0secs )
remaining_time = 0secs;
@if ( ! Cluster::is_enabled() || ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) )
if ( remaining_time == 0secs )
{
local log = populate_log_record(idx, t[idx], FORGOTTEN);
Log::write(CATCH_RELEASE, log);
}
@endif
return remaining_time;
}
# This is the internally maintained table containing all the addresses that are currently being
# watched to see if they will re-surface. After the time is reached, monitoring of that specific
# IP will stop.
global blocks: table[addr] of BlockInfo = {}
&create_expire=0secs
&expire_func=per_block_interval;
function current_block_interval(s: set[addr], idx: addr): interval
@if ( Cluster::is_enabled() )
@load base/frameworks/cluster
redef Cluster::manager2worker_events += /NetControl::catch_release_block_(new|delete)/;
redef Cluster::worker2manager_events += /NetControl::catch_release_(add|delete|encountered)/;
@endif
function cr_check_rule(r: Rule): bool
{
if ( idx !in blocks )
if ( r$ty == DROP && r$entity$ty == ADDRESS )
{
Reporter::error(fmt("Address %s not in blocks while inserting into current_blocks!", idx));
return 0sec;
local ip = r$entity$ip;
if ( ( is_v4_subnet(ip) && subnet_width(ip) == 32 ) || ( is_v6_subnet(ip) && subnet_width(ip) == 128 ) )
{
if ( subnet_to_addr(ip) in blocks )
return T;
}
}
return catch_release_intervals[blocks[idx]];
return F;
}
global current_blocks: set[addr] = set()
&create_expire=0secs
&expire_func=current_block_interval;
@if ( ! Cluster::is_enabled() || ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) )
function drop_address_catch_release(a: addr, location: string &default=""): string
event rule_added(r: Rule, p: PluginState, msg: string &default="")
{
if ( !cr_check_rule(r) )
return;
local ip = subnet_to_addr(r$entity$ip);
local bi = blocks[ip];
local log = populate_log_record(ip, bi, DROPPED);
if ( msg != "" )
log$message = msg;
Log::write(CATCH_RELEASE, log);
}
event rule_timeout(r: Rule, i: FlowInfo, p: PluginState)
{
if ( !cr_check_rule(r) )
return;
local ip = subnet_to_addr(r$entity$ip);
local bi = blocks[ip];
local log = populate_log_record(ip, bi, UNBLOCK);
if ( bi?$block_until )
{
local difference: interval = network_time() - bi$block_until;
if ( interval_to_double(difference) > 60 || interval_to_double(difference) < -60 )
log$message = fmt("Difference between network_time and block time excessive: %f", difference);
}
Log::write(CATCH_RELEASE, log);
}
@endif
@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER )
event catch_release_add(a: addr, location: string)
{
drop_address_catch_release(a, location);
}
event catch_release_delete(a: addr)
{
unblock_address_catch_release(a);
}
event catch_release_encountered(a: addr)
{
catch_release_seen(a);
}
@endif
@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER )
event catch_release_block_new(a: addr, b: BlockInfo)
{
blocks[a] = b;
}
event catch_release_block_delete(a: addr)
{
if ( a in blocks )
delete blocks[a];
}
@endif
@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER )
@endif
function get_catch_release_info(a: addr): BlockInfo
{
if ( a in blocks )
return blocks[a];
return BlockInfo($watch_until=double_to_time(0), $current_interval=0, $current_block_id="");
}
function drop_address_catch_release(a: addr, location: string &default=""): BlockInfo
{
local bi: BlockInfo;
local log: CatchReleaseInfo;
if ( a in blocks )
{
Reporter::warning(fmt("Address %s already blocked using catch-and-release - ignoring duplicate", a));
return "";
log = populate_log_record(a, blocks[a], INFO);
log$message = "Already blocked using catch-and-release - ignoring duplicate";
Log::write(CATCH_RELEASE, log);
return blocks[a];
}
local e = Entity($ty=ADDRESS, $ip=addr_to_subnet(a));
if ( [e,DROP] in rule_entities )
{
local r = rule_entities[e,DROP];
bi = BlockInfo($watch_until=network_time()+catch_release_intervals[1], $current_interval=0, $current_block_id=r$id);
if ( location != "" )
bi$location = location;
@if ( ! Cluster::is_enabled() || ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) )
log = populate_log_record(a, bi, ADDED);
log$message = "Address already blocked outside of catch-and-release. Catch and release will monitor and only actively block if it appears in network traffic.";
Log::write(CATCH_RELEASE, log);
blocks[a] = bi;
event NetControl::catch_release_block_new(a, bi);
@endif
@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER )
event NetControl::catch_release_add(a, location);
@endif
return bi;
}
local block_interval = catch_release_intervals[0];
@if ( ! Cluster::is_enabled() || ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) )
local ret = drop_address(a, block_interval, location);
if ( ret != "" )
{
blocks[a] = 0;
add current_blocks[a];
bi = BlockInfo($watch_until=network_time()+catch_release_intervals[1], $block_until=network_time()+block_interval, $current_interval=0, $current_block_id=ret);
if ( location != "" )
bi$location = location;
blocks[a] = bi;
event NetControl::catch_release_block_new(a, bi);
blocks[a] = bi;
log = populate_log_record(a, bi, DROP);
Log::write(CATCH_RELEASE, log);
return bi;
}
Reporter::error(fmt("Catch and release could not add block for %s; failing.", a));
return BlockInfo($watch_until=double_to_time(0), $current_interval=0, $current_block_id="");
@endif
@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER )
bi = BlockInfo($watch_until=network_time()+catch_release_intervals[1], $block_until=network_time()+block_interval, $current_interval=0, $current_block_id="");
event NetControl::catch_release_add(a, location);
return bi;
@endif
return ret;
}
function check_conn(a: addr)
function unblock_address_catch_release(a: addr): bool
{
if ( a !in blocks )
return F;
@if ( ! Cluster::is_enabled() || ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) )
local bi = blocks[a];
local log = populate_log_record(a, bi, UNBLOCK);
Log::write(CATCH_RELEASE, log);
delete blocks[a];
if ( bi?$block_until && bi$block_until > network_time() && bi$current_block_id != "" )
remove_rule(bi$current_block_id);
@endif
@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER )
event NetControl::catch_release_block_delete(a);
@endif
@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER )
event NetControl::catch_release_delete(a);
@endif
return T;
}
function catch_release_seen(a: addr)
{
local e = Entity($ty=ADDRESS, $ip=addr_to_subnet(a));
if ( a in blocks )
{
if ( a in current_blocks )
# block has not been applied yet?
@if ( ! Cluster::is_enabled() || ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER ) )
local bi = blocks[a];
local log: CatchReleaseInfo;
if ( [e,DROP] in rule_entities )
{
if ( catch_release_warn_blocked_ip_encountered == F )
return;
# This should be blocked - block has not been applied yet by hardware? Ignore for the moment...
log = populate_log_record(a, bi, INFO);
log$action = INFO;
log$message = "Block seen while in rule_entities. No action taken.";
Log::write(CATCH_RELEASE, log);
return;
}
# ok, this one returned again while still in the backoff period.
local try = blocks[a];
local try = bi$current_interval;
if ( (try+1) in catch_release_intervals )
++try;
blocks[a] = try;
add current_blocks[a];
bi$current_interval = try;
if ( (try+1) in catch_release_intervals )
bi$watch_until = network_time() + catch_release_intervals[try+1];
else
bi$watch_until = network_time() + catch_release_intervals[try];
bi$block_until = network_time() + catch_release_intervals[try];
++bi$num_reblocked;
local block_interval = catch_release_intervals[try];
drop_address(a, block_interval, "Re-drop by catch-and-release");
local location = "";
if ( bi?$location )
location = bi$location;
local drop = drop_address(a, block_interval, fmt("Re-drop by catch-and-release: %s", location));
bi$current_block_id = drop;
blocks[a] = bi;
log = populate_log_record(a, bi, SEEN_AGAIN);
Log::write(CATCH_RELEASE, log);
@endif
@if ( Cluster::is_enabled() && Cluster::local_node_type() == Cluster::MANAGER )
event NetControl::catch_release_block_new(a, bi);
@endif
@if ( Cluster::is_enabled() && Cluster::local_node_type() != Cluster::MANAGER )
if ( a in catch_release_recently_notified )
return;
event NetControl::catch_release_encountered(a);
add catch_release_recently_notified[a];
@endif
return;
}
return;
}
event new_connection(c: connection)
{
# let's only check originating connections...
check_conn(c$id$orig_h);
if ( watch_connections )
catch_release_seen(c$id$orig_h);
}
event connection_established(c: connection)
{
if ( watch_connections )
catch_release_seen(c$id$orig_h);
}
event partial_connection(c: connection)
{
if ( watch_connections )
catch_release_seen(c$id$orig_h);
}
event connection_attempt(c: connection)
{
if ( watch_connections )
catch_release_seen(c$id$orig_h);
}
event connection_rejected(c: connection)
{
if ( watch_connections )
catch_release_seen(c$id$orig_h);
}
event connection_reset(c: connection)
{
if ( watch_connections )
catch_release_seen(c$id$orig_h);
}
event connection_pending(c: connection)
{
if ( watch_connections )
catch_release_seen(c$id$orig_h);
}

View file

@ -11,13 +11,15 @@ export {
## This is the event used to transport remove_rule calls to the manager.
global cluster_netcontrol_remove_rule: event(id: string);
## This is the event used to transport delete_rule calls to the manager.
global cluster_netcontrol_delete_rule: event(id: string);
}
## Workers need ability to forward commands to manager.
redef Cluster::worker2manager_events += /NetControl::cluster_netcontrol_(add|remove)_rule/;
redef Cluster::worker2manager_events += /NetControl::cluster_netcontrol_(add|remove|delete)_rule/;
## Workers need to see the result events from the manager.
redef Cluster::manager2worker_events += /NetControl::rule_(added|removed|timeout|error)/;
redef Cluster::manager2worker_events += /NetControl::rule_(added|removed|timeout|error|exists|new|destroyed)/;
function activate(p: PluginState, priority: int)
{
@ -36,6 +38,16 @@ function add_rule(r: Rule) : string
return add_rule_impl(r);
else
{
# we sync rule entities accross the cluster, so we
# acually can test if the rule already exists. If yes,
# refuse insertion already at the node.
if ( [r$entity, r$ty] in rule_entities )
{
log_rule_no_plugin(r, FAILED, "discarded duplicate insertion");
return "";
}
if ( r$id == "" )
r$id = cat(Cluster::node, ":", ++local_rule_count);
@ -44,6 +56,17 @@ function add_rule(r: Rule) : string
}
}
function delete_rule(id: string) : bool
{
if ( Cluster::local_node_type() == Cluster::MANAGER )
return delete_rule_impl(id);
else
{
event NetControl::cluster_netcontrol_delete_rule(id);
return T; # well, we can't know here. So - just hope...
}
}
function remove_rule(id: string) : bool
{
if ( Cluster::local_node_type() == Cluster::MANAGER )
@ -56,6 +79,11 @@ function remove_rule(id: string) : bool
}
@if ( Cluster::local_node_type() == Cluster::MANAGER )
event NetControl::cluster_netcontrol_delete_rule(id: string)
{
delete_rule_impl(id);
}
event NetControl::cluster_netcontrol_add_rule(r: Rule)
{
add_rule_impl(r);
@ -65,17 +93,23 @@ event NetControl::cluster_netcontrol_remove_rule(id: string)
{
remove_rule_impl(id);
}
@endif
@if ( Cluster::local_node_type() == Cluster::MANAGER )
event rule_expire(r: Rule, p: PluginState) &priority=-5
{
rule_expire_impl(r, p);
}
event rule_exists(r: Rule, p: PluginState, msg: string &default="") &priority=5
{
rule_added_impl(r, p, T, msg);
if ( r?$expire && r$expire > 0secs && ! p$plugin$can_expire )
schedule r$expire { rule_expire(r, p) };
}
event rule_added(r: Rule, p: PluginState, msg: string &default="") &priority=5
{
rule_added_impl(r, p, msg);
rule_added_impl(r, p, F, msg);
if ( r?$expire && r$expire > 0secs && ! p$plugin$can_expire )
schedule r$expire { rule_expire(r, p) };
@ -97,3 +131,30 @@ event rule_error(r: Rule, p: PluginState, msg: string &default="") &priority=-5
}
@endif
# Workers use the events to keep track in their local state tables
@if ( Cluster::local_node_type() != Cluster::MANAGER )
event rule_new(r: Rule) &priority=5
{
if ( r$id in rules )
return;
rules[r$id] = r;
rule_entities[r$entity, r$ty] = r;
add_subnet_entry(r);
}
event rule_destroyed(r: Rule) &priority=5
{
if ( r$id !in rules )
return;
remove_subnet_entry(r);
if ( [r$entity, r$ty] in rule_entities )
delete rule_entities[r$entity, r$ty];
delete rules[r$id];
}
@endif

View file

@ -44,6 +44,12 @@ export {
location: string &log &optional;
};
## Hook that allows the modification of rules passed to drop_* before they
## are passed on. If one of the hooks uses break, the rule is ignored.
##
## r: The rule to be added
global NetControl::drop_rule_policy: hook(r: Rule);
## Event that can be handled to access the :bro:type:`NetControl::ShuntInfo`
## record as it is sent on to the logging framework.
global log_netcontrol_drop: event(rec: DropInfo);
@ -59,6 +65,9 @@ function drop_connection(c: conn_id, t: interval, location: string &default="")
local e: Entity = [$ty=CONNECTION, $conn=c];
local r: Rule = [$ty=DROP, $target=FORWARD, $entity=e, $expire=t, $location=location];
if ( ! hook NetControl::drop_rule_policy(r) )
return "";
local id = add_rule(r);
# Error should already be logged
@ -80,6 +89,9 @@ function drop_address(a: addr, t: interval, location: string &default="") : stri
local e: Entity = [$ty=ADDRESS, $ip=addr_to_subnet(a)];
local r: Rule = [$ty=DROP, $target=FORWARD, $entity=e, $expire=t, $location=location];
if ( ! hook NetControl::drop_rule_policy(r) )
return "";
local id = add_rule(r);
# Error should already be logged

View file

@ -1,4 +1,4 @@
##! Bro's packet aquisition and control framework.
##! Bro's NetControl 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
@ -81,9 +81,11 @@ export {
## 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.
## Quarantines a host. This requires a special quarantine server, which runs a HTTP server explaining
## the quarantine and a DNS server which resolves all requests to the quarantine server. DNS queries
## from the host to the network DNS server will be rewritten and will be sent to the quarantine server
## instead. Only http communication infected to quarantinehost is allowed. All other network communication
## is blocked.
##
## infected: the host to quarantine
##
@ -96,7 +98,7 @@ export {
## 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 &default="") : vector of string;
## Flushes all state.
## Flushes all state by calling :bro:see:`NetControl::remove_rule` on all currently active rules.
global clear: function();
# ###
@ -120,7 +122,7 @@ export {
## Removes a rule.
##
## id: The rule to remove, specified as the ID returned by :bro:id:`NetControl::add_rule`.
## id: The rule to remove, specified as the ID returned by :bro:see:`NetControl::add_rule`.
##
## Returns: True if succesful, the relevant plugin indicated that it knew
## how to handle the removal. Note that again "success" means the
@ -129,8 +131,23 @@ export {
## wrong at that point.
global remove_rule: function(id: string) : bool;
## Deletes a rule without removing in from the backends to which it has been
## added before. This mean that no messages will be sent to the switches to which
## the rule has been added; if it is not removed from them by a separate mechanism,
## it will stay installed and not be removed later.
##
## id: The rule to delete, specified as the ID returned by :bro:see:`add_rule` .
##
## Returns: True if removal is successful, or sent to manager.
## False if the rule could not be found.
global delete_rule: function(id: string) : bool;
## Searches all rules affecting a certain IP address.
##
## This function works on both the manager and workers of a cluster. Note that on
## the worker, the internal rule variables (starting with _) will not reflect the
## current state.
##
## ip: The ip address to search for
##
## Returns: vector of all rules affecting the IP address
@ -138,6 +155,18 @@ export {
## Searches all rules affecting a certain subnet.
##
## A rule affects a subnet, if it covers the whole subnet. Note especially that
## this function will not reveal all rules that are covered by a subnet.
##
## For example, a search for 192.168.17.0/8 will reveal a rule that exists for
## 192.168.0.0/16, since this rule affects the subnet. However, it will not reveal
## a more specific rule for 192.168.17.1/32, which does not directy affect the whole
## subnet.
##
## This function works on both the manager and workers of a cluster. Note that on
## the worker, the internal rule variables (starting with _) will not reflect the
## current state.
##
## sn: The subnet to search for
##
## Returns: vector of all rules affecting the subnet
@ -145,7 +174,7 @@ export {
###### Asynchronous feedback on rules.
## Confirms that a rule was put in place.
## Confirms that a rule was put in place by a plugin.
##
## r: The rule now in place.
##
@ -154,7 +183,21 @@ export {
## 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.
## Signals that a rule that was supposed to be put in place was already
## existing at the specified plugin. Rules that already have been existing
## continue to be tracked like normal, but no timeout calls will be sent
## to the specified plugins. Removal of the rule from the hardware can
## still be forced by manually issuing a remove_rule call.
##
## r: The rule that was already in place.
##
## p: The plugin that reported that the rule already was in place.
##
## msg: An optional informational message by the plugin.
global rule_exists: event(r: Rule, p: PluginState, msg: string &default="");
## Reports that a plugin reports a rule was removed due to a
## remove: function() vall.
##
## r: The rule now removed.
##
@ -164,7 +207,7 @@ export {
## 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.
## Reports that a rule was removed from a plugin due to a timeout.
##
## r: The rule now removed.
##
@ -185,6 +228,26 @@ export {
## msg: An optional informational message by the plugin.
global rule_error: event(r: Rule, p: PluginState, msg: string &default="");
## This event is raised when a new rule is created by the NetControl framework
## due to a call to add_rule. From this moment, until the rule_destroyed event
## is raised, the rule is tracked internally by the NetControl framewory.
##
## Note that this event does not mean that a rule was succesfully added by
## any backend; it just means that the rule has been accepted and addition
## to the specified backend is queued. To get information when rules are actually
## installed by the hardware, use the rule_added, rule_exists, rule_removed, rule_timeout
## and rule_error events.
global rule_new: event(r: Rule);
## This event is raised when a rule is deleted from the NetControl framework,
## because it is no longer in use. This can be caused by the fact that a rule
## was removed by all plugins to which it was added, by the fact that it timed out
## or due to rule errors.
##
## To get the cause or a rule remove, hook the rule_removed, rule_timeout and
## rule_error calls.
global rule_destroyed: event(r: Rule);
## 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.
@ -206,17 +269,18 @@ export {
MESSAGE,
## A log entry reflecting a framework message.
ERROR,
## A log entry about about a rule.
## A log entry about a rule.
RULE
};
## State of an entry in the NetControl log.
## State of an entry in the NetControl log.
type InfoState: enum {
REQUESTED,
SUCCEEDED,
FAILED,
REMOVED,
TIMEOUT,
REQUESTED, ##< The request to add/remove a rule was sent to the respective backend
SUCCEEDED, ##< A rule was succesfully added by a backend
EXISTS, ##< A backend reported that a rule was already existing
FAILED, ##< A rule addition failed
REMOVED, ##< A rule was succesfully removed by a backend
TIMEOUT, ##< A rule timeout was triggered by the NetControl framework or a backend
};
## The record type defining the column fields of the NetControl log.
@ -259,11 +323,13 @@ export {
}
redef record Rule += {
##< Internally set to the plugins handling the rule.
## Internally set to the plugins handling the rule.
_plugin_ids: set[count] &default=count_set();
##< Internally set to the plugins on which the rule is currently active.
## Internally set to the plugins on which the rule is currently active.
_active_plugin_ids: set[count] &default=count_set();
##< Track if the rule was added succesfully by all responsible plugins.
## Internally set to plugins where the rule should not be removed upon timeout.
_no_expire_plugins: set[count] &default=count_set();
## Track if the rule was added succesfully by all responsible plugins.
_added: bool &default=F;
};
@ -535,6 +601,11 @@ function plugin_activated(p: PluginState)
log_error("unknown plugin activated", p);
return;
}
# Suppress duplicate activation
if ( plugin_ids[id]$_activated == T )
return;
plugin_ids[id]$_activated = T;
log_msg("activation finished", p);
@ -727,6 +798,8 @@ function add_rule_impl(rule: Rule) : string
add_subnet_entry(rule);
event NetControl::rule_new(rule);
return rule$id;
}
@ -734,6 +807,32 @@ function add_rule_impl(rule: Rule) : string
return "";
}
function rule_cleanup(r: Rule)
{
if ( |r$_active_plugin_ids| > 0 )
return;
remove_subnet_entry(r);
delete rule_entities[r$entity, r$ty];
delete rules[r$id];
event NetControl::rule_destroyed(r);
}
function delete_rule_impl(id: string): bool
{
if ( id !in rules )
return F;
local rule = rules[id];
rule$_active_plugin_ids = set();
rule_cleanup(rule);
return T;
}
function remove_rule_plugin(r: Rule, p: PluginState): bool
{
local success = T;
@ -782,10 +881,21 @@ function rule_expire_impl(r: Rule, p: PluginState) &priority=-5
# Removed already.
return;
event NetControl::rule_timeout(r, FlowInfo(), p); # timeout implementation will handle the removal
local rule = rules[r$id];
if ( p$_id in rule$_no_expire_plugins )
{
# in this case - don't log anything, just remove the plugin from the rule
# and cleaup
delete rule$_active_plugin_ids[p$_id];
delete rule$_no_expire_plugins[p$_id];
rule_cleanup(rule);
}
else
event NetControl::rule_timeout(r, FlowInfo(), p); # timeout implementation will handle the removal
}
function rule_added_impl(r: Rule, p: PluginState, msg: string &default="")
function rule_added_impl(r: Rule, p: PluginState, exists: bool, msg: string &default="")
{
if ( r$id !in rules )
{
@ -801,7 +911,15 @@ function rule_added_impl(r: Rule, p: PluginState, msg: string &default="")
return;
}
log_rule(r, "ADD", SUCCEEDED, p, msg);
# The rule was already existing on the backend. Mark this so we don't timeout
# it on this backend.
if ( exists )
{
add rule$_no_expire_plugins[p$_id];
log_rule(r, "ADD", EXISTS, p, msg);
}
else
log_rule(r, "ADD", SUCCEEDED, p, msg);
add rule$_active_plugin_ids[p$_id];
if ( |rule$_plugin_ids| == |rule$_active_plugin_ids| )
@ -811,17 +929,6 @@ function rule_added_impl(r: Rule, p: PluginState, msg: string &default="")
}
}
function rule_cleanup(r: Rule)
{
if ( |r$_active_plugin_ids| > 0 )
return;
remove_subnet_entry(r);
delete rule_entities[r$entity, r$ty];
delete rules[r$id];
}
function rule_removed_impl(r: Rule, p: PluginState, msg: string &default="")
{
if ( r$id !in rules )

View file

@ -12,6 +12,11 @@ function add_rule(r: Rule) : string
return add_rule_impl(r);
}
function delete_rule(id: string) : bool
{
return delete_rule_impl(id);
}
function remove_rule(id: string) : bool
{
return remove_rule_impl(id);
@ -22,9 +27,17 @@ event rule_expire(r: Rule, p: PluginState) &priority=-5
rule_expire_impl(r, p);
}
event rule_exists(r: Rule, p: PluginState, msg: string &default="") &priority=5
{
rule_added_impl(r, p, T, msg);
if ( r?$expire && r$expire > 0secs && ! p$plugin$can_expire )
schedule r$expire { rule_expire(r, p) };
}
event rule_added(r: Rule, p: PluginState, msg: string &default="") &priority=5
{
rule_added_impl(r, p, msg);
rule_added_impl(r, p, F, msg);
if ( r?$expire && r$expire > 0secs && ! p$plugin$can_expire )
schedule r$expire { rule_expire(r, p) };

View file

@ -1,11 +1,13 @@
##! Plugin interface for NetControl backends.
##! This file defines the plugin interface for NetControl.
module NetControl;
@load ./types
export {
## State for a plugin instance.
## This record keeps the per instance state of a plugin.
##
## Individual plugins commonly extend this record to suit their needs.
type PluginState: record {
## Table for a plugin to store custom, instance-specfific state.
config: table[string] of string &default=table();
@ -20,69 +22,69 @@ export {
_activated: bool &default=F;
};
# 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 inform 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.
## 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 inform 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.
## 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,
## If true, plugin can expire rules itself. If false, the NetControl
## framework will manage rule expiration.
can_expire: bool;
# One-time initialization function called when plugin gets registered, and
# before any other methods are called.
#
# If this function is provided, NetControl assumes that the plugin has to
# perform, potentially lengthy, initialization before the plugin will become
# active. In this case, the plugin has to call ``NetControl::plugin_activated``,
# once initialization finishes.
## One-time initialization function called when plugin gets registered, and
## before any other methods are called.
##
## If this function is provided, NetControl assumes that the plugin has to
## perform, potentially lengthy, initialization before the plugin will become
## active. In this case, the plugin has to call ``NetControl::plugin_activated``,
## once initialization finishes.
init: function(state: PluginState) &optional;
# One-time finalization function called when a plugin is shutdown; no further
# functions will be called afterwords.
## 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.
## 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().
## 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.
## 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.
## 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.)
## because of cyclic type dependency.)
plugin: Plugin &optional;
};

View file

@ -0,0 +1 @@
Plugins for the NetControl framework

View file

@ -66,6 +66,7 @@ export {
## Events that are sent from Broker to us
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_exists: event(id: count, r: Rule, msg: string);
global acld_rule_error: event(id: count, r: Rule, msg: string);
}
@ -76,7 +77,7 @@ global netcontrol_acld_current_id: count = 0;
const acld_add_to_remove: table[string] of string = {
["drop"] = "restore",
["whitelist"] = "remwhitelist",
["addwhitelist"] = "remwhitelist",
["blockhosthost"] = "restorehosthost",
["droptcpport"] = "restoretcpport",
["dropudpport"] = "restoreudpport",
@ -100,6 +101,19 @@ event NetControl::acld_rule_added(id: count, r: Rule, msg: string)
event NetControl::rule_added(r, p, msg);
}
event NetControl::acld_rule_exists(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_exists(r, p, msg);
}
event NetControl::acld_rule_removed(id: count, r: Rule, msg: string)
{
if ( id !in netcontrol_acld_id )
@ -155,7 +169,7 @@ function rule_to_acl_rule(p: PluginState, r: Rule) : AclRule
if ( r$ty == DROP )
command = "drop";
else if ( r$ty == WHITELIST )
command = "whitelist";
command = "addwhitelist";
arg = cat(e$ip);
}
else if ( e$ty == FLOW )

View file

@ -11,18 +11,38 @@ module NetControl;
@ifdef ( Broker::__enable )
export {
## This record specifies the configuration that is passed to :bro:see:`NetControl::create_broker`.
type BrokerConfig: record {
## The broker topic used to send events to
topic: string &optional;
## Broker host to connect to
host: addr &optional;
## Broker port to connect to
bport: port &optional;
## Do we accept rules for the monitor path? Default true
monitor: bool &default=T;
## Do we accept rules for the forward path? Default true
forward: bool &default=T;
## Predicate that is called on rule insertion or removal.
##
## p: Current plugin state
##
## r: The rule to be inserted or removed
##
## Returns: T if the rule can be handled by the current backend, F otherwhise
check_pred: function(p: PluginState, r: Rule): bool &optional;
};
## Instantiates the broker plugin.
global create_broker: function(host: addr, host_port: port, topic: string, can_expire: bool &default=F) : PluginState;
global create_broker: function(config: BrokerConfig, can_expire: bool) : PluginState;
redef record PluginState += {
## The broker topic used to send events to
broker_topic: string &optional;
## OpenFlow controller for NetControl Broker plugin
broker_config: BrokerConfig &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);
@ -30,6 +50,7 @@ export {
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_exists: 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);
}
@ -52,6 +73,19 @@ event NetControl::broker_rule_added(id: count, r: Rule, msg: string)
event NetControl::rule_added(r, p, msg);
}
event NetControl::broker_rule_exists(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_exists(r, p, msg);
}
event NetControl::broker_rule_removed(id: count, r: Rule, msg: string)
{
if ( id !in netcontrol_broker_id )
@ -93,26 +127,48 @@ event NetControl::broker_rule_timeout(id: count, r: Rule, i: FlowInfo)
function broker_name(p: PluginState) : string
{
return fmt("Broker-%s", p$broker_topic);
return fmt("Broker-%s", p$broker_config$topic);
}
function broker_check_rule(p: PluginState, r: Rule) : bool
{
local c = p$broker_config;
if ( p$broker_config?$check_pred )
return p$broker_config$check_pred(p, r);
if ( r$target == MONITOR && c$monitor )
return T;
if ( r$target == FORWARD && c$forward )
return T;
return F;
}
function broker_add_rule_fun(p: PluginState, r: Rule) : bool
{
Broker::send_event(p$broker_topic, Broker::event_args(broker_add_rule, p$broker_id, r));
if ( ! broker_check_rule(p, r) )
return F;
Broker::send_event(p$broker_config$topic, Broker::event_args(broker_add_rule, p$broker_id, r));
return T;
}
function broker_remove_rule_fun(p: PluginState, r: Rule) : bool
{
Broker::send_event(p$broker_topic, Broker::event_args(broker_remove_rule, p$broker_id, r));
if ( ! broker_check_rule(p, r) )
return F;
Broker::send_event(p$broker_config$topic, Broker::event_args(broker_remove_rule, p$broker_id, r));
return T;
}
function broker_init(p: PluginState)
{
Broker::enable();
Broker::connect(cat(p$broker_host), p$broker_port, 1sec);
Broker::subscribe_to_events(p$broker_topic);
Broker::connect(cat(p$broker_config$host), p$broker_config$bport, 1sec);
Broker::subscribe_to_events(p$broker_config$topic);
}
event Broker::outgoing_connection_established(peer_address: string, peer_port: port, peer_name: string)
@ -140,23 +196,23 @@ global broker_plugin_can_expire = Plugin(
$init = broker_init
);
function create_broker(host: addr, host_port: port, topic: string, can_expire: bool &default=F) : PluginState
function create_broker(config: BrokerConfig, can_expire: bool) : PluginState
{
if ( topic in netcontrol_broker_topics )
Reporter::warning(fmt("Topic %s was added to NetControl broker plugin twice. Possible duplication of commands", topic));
if ( config$topic in netcontrol_broker_topics )
Reporter::warning(fmt("Topic %s was added to NetControl broker plugin twice. Possible duplication of commands", config$topic));
else
add netcontrol_broker_topics[topic];
add netcontrol_broker_topics[config$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];
local p = PluginState($plugin=plugin, $broker_id=netcontrol_broker_current_id, $broker_config=config);
if ( [host_port, cat(host)] in netcontrol_broker_peers )
Reporter::warning(fmt("Peer %s:%s was added to NetControl broker plugin twice.", host, host_port));
if ( [config$bport, cat(config$host)] in netcontrol_broker_peers )
Reporter::warning(fmt("Peer %s:%s was added to NetControl broker plugin twice.", config$host, config$bport));
else
netcontrol_broker_peers[host_port, cat(host)] = p;
netcontrol_broker_peers[config$bport, cat(config$host)] = p;
netcontrol_broker_id[netcontrol_broker_current_id] = p;
++netcontrol_broker_current_id;

View file

@ -7,22 +7,46 @@
module NetControl;
export {
## This record specifies the configuration that is passed to :bro:see:`NetControl::create_openflow`.
type OfConfig: record {
monitor: bool &default=T;
forward: bool &default=T;
idle_timeout: count &default=0;
table_id: count &optional;
monitor: bool &default=T; ##< accept rules that target the monitor path
forward: bool &default=T; ##< accept rules that target the forward path
idle_timeout: count &default=0; ##< default OpenFlow idle timeout
table_id: count &optional; ##< default OpenFlow table ID.
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.
## Predicate that is called on rule insertion or removal.
##
## p: Current plugin state
## p: Current plugin state.
##
## r: The rule to be inserted or removed
## r: The rule to be inserted or removed.
##
## Returns: T if the rule can be handled by the current backend, F otherwhise
## Returns: T if the rule can be handled by the current backend, F otherwhise.
check_pred: function(p: PluginState, r: Rule): bool &optional;
## This predicate is called each time an OpenFlow match record is created.
## The predicate can modify the match structure before it is sent on to the
## device.
##
## p: Current plugin state.
##
## r: The rule to be inserted or removed.
##
## m: The openflow match structures that were generated for this rules.
##
## Returns: The modified OpenFlow match structures that will be used in place the structures passed in m.
match_pred: function(p: PluginState, e: Entity, m: vector of OpenFlow::ofp_match): vector of OpenFlow::ofp_match &optional;
## This predicate is called before an FlowMod message is sent to the OpenFlow
## device. It can modify the FlowMod message before it is passed on.
##
## p: Current plugin state.
##
## r: The rule to be inserted or removed.
##
## m: The OpenFlow FlowMod message.
##
## Returns: The modified FloMod message that is used in lieu of m.
flow_mod_pred: function(p: PluginState, r: Rule, m: OpenFlow::ofp_flow_mod): OpenFlow::ofp_flow_mod &optional;
};

View file

@ -1,30 +1,45 @@
##! Types used by the NetControl framework.
##! This file defines the that are used by the NetControl framework.
##!
##! The most important type defined in this file is :bro:see:`NetControl::Rule`,
##! which is used to describe all rules that can be expressed by the NetControl framework.
module NetControl;
export {
## The default priority that is used when creating rules.
const default_priority: int = +0 &redef;
## The default priority that is used when using the high-level functions to
## push whitelist entries to the backends (:bro:see:`NetControl::whitelist_address` and
## :bro:see:`NetControl::whitelist_subnet`).
##
## Note that this priority is not automatically used when manually creating rules
## that have a :bro:see:`NetControl::RuleType` of :bro:enum:`NetControl::WHITELIST`.
const whitelist_priority: int = +5 &redef;
## Type of a :bro:id:`Entity` for defining an action.
## The EntityType is used in :bro:id:`Entity` for defining the entity that a rule
## applies to.
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.
CONNECTION, ##< Activity involving all of a bi-directional connection's activity.
FLOW, ##< Actitivy involving a uni-directional flow's activity. Can contain wildcards.
MAC, ##< Activity involving a MAC address.
};
## Type for defining a flow.
## Flow is used in :bro:id:`Entity` together with :bro:enum:`NetControl::FLOW` to specify
## a uni-directional flow that a :bro:id:`Rule` applies to.
##
## If optional fields are not set, they are interpreted as wildcarded.
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.
dst_p: port &optional; ##< The destination 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 defining the entity an :bro:id:`Rule` is operating on.
type Entity: record {
ty: EntityType; ##< Type of entity.
conn: conn_id &optional; ##< Used with :bro:enum:`NetControl::CONNECTION`.
@ -33,32 +48,36 @@ export {
mac: string &optional; ##< Used with :bro:enum:`NetControl::MAC`.
};
## Target of :bro:id:`Rule` action.
## The :bro:id`TargetType` defined the target of a :bro:id:`Rule`.
##
## Rules can either be applied to the forward path, affecting all network traffic, or
## on the monitor path, only affecting the traffic that is sent to Bro. The second
## is mostly used for shunting, which allows Bro to tell the networking hardware that
## it wants to no longer see traffic that it identified as benign.
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
## Type of rules that the framework supports. Each type lists the extra
## :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.
## Stop forwarding all packets matching the entity.
##
## No arguments.
## No additional arguments.
DROP,
## Begin modifying all packets matching entity.
## Modify all packets matching entity. The packets
## will be modified according to the `mod` entry of
## the rule.
##
## .. todo::
## Define arguments.
MODIFY,
## Begin redirecting all packets matching entity.
## Redirect all packets matching entity to a different switch port,
## given in the `out_port` argument of the rule.
##
## .. todo::
## c: output port to redirect traffic to.
REDIRECT,
## Whitelists all packets of an entity, meaning no restrictions will be applied.