mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 14:48:21 +00:00
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:
commit
09ea84bb6e
115 changed files with 3072 additions and 301 deletions
3
scripts/base/frameworks/netcontrol/README
Normal file
3
scripts/base/frameworks/netcontrol/README
Normal 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.
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -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) };
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
1
scripts/base/frameworks/netcontrol/plugins/README
Normal file
1
scripts/base/frameworks/netcontrol/plugins/README
Normal file
|
@ -0,0 +1 @@
|
|||
Plugins for the NetControl framework
|
|
@ -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 )
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue