diff --git a/scripts/base/frameworks/pacf/__load__.bro b/scripts/base/frameworks/pacf/__load__.bro index 033c30f9a0..7bfd6aed7e 100644 --- a/scripts/base/frameworks/pacf/__load__.bro +++ b/scripts/base/frameworks/pacf/__load__.bro @@ -1,6 +1,7 @@ @load ./types @load ./main @load ./plugins +@load ./catch-and-release # The cluster framework must be loaded first. @load base/frameworks/cluster diff --git a/scripts/base/frameworks/pacf/catch-and-release.bro b/scripts/base/frameworks/pacf/catch-and-release.bro new file mode 100644 index 0000000000..fceefadb5c --- /dev/null +++ b/scripts/base/frameworks/pacf/catch-and-release.bro @@ -0,0 +1,99 @@ +##! Implementation of catch-and-release functionality for Pacf + +module Pacf; + +export { + ## Stops all packets involving an IP address from being forwarded. This function + ## uses catch-and-release functionality, where the IP address is only dropped for + ## a short amount of time that is incremented steadily when the IP is encountered + ## again. + ## + ## a: The address to be dropped. + ## + ## t: How long to drop it, with 0 being indefinitly. + ## + ## location: An optional string describing where the drop was triggered. + ## + ## Returns: The id of the inserted rule on succes and zero on failure. + global drop_address_catch_release: function(a: addr, location: string &default="") : string; + + const catch_release_intervals: vector of interval = vector(10min, 1hr, 24hrs, 7days) &redef; +} + +function per_block_interval(t: table[addr] of count, idx: addr): interval + { + local ct = t[idx]; + + # watch for the time of the next block... + local blocktime = catch_release_intervals[ct]; + if ( (ct+1) in catch_release_intervals ) + blocktime = catch_release_intervals[ct+1]; + + return blocktime; + } + +# This is the internally maintained table containing all the currently going on catch-and-release +# blocks. +global blocks: table[addr] of count = {} + &create_expire=0secs + &expire_func=per_block_interval; + +function current_block_interval(s: set[addr], idx: addr): interval + { + if ( idx !in blocks ) + { + Reporter::error(fmt("Address %s not in blocks while inserting into current_blocks!", idx)); + return 0sec; + } + + return catch_release_intervals[blocks[idx]]; + } + +global current_blocks: set[addr] = set() + &create_expire=0secs + &expire_func=current_block_interval; + +function drop_address_catch_release(a: addr, location: string &default=""): string + { + if ( a in blocks ) + { + Reporter::warning(fmt("Address %s already blocked using catch-and-release - ignoring duplicate", a)); + return ""; + } + + local block_interval = catch_release_intervals[0]; + local ret = drop_address(a, block_interval, location); + if ( ret != "" ) + { + blocks[a] = 0; + add current_blocks[a]; + } + + return ret; + } + +function check_conn(a: addr) + { + if ( a in blocks ) + { + if ( a in current_blocks ) + # block has not been applied yet? + return; + + # ok, this one returned again while still in the backoff period. + local try = blocks[a]; + if ( (try+1) in catch_release_intervals ) + ++try; + + blocks[a] = try; + add current_blocks[a]; + local block_interval = catch_release_intervals[try]; + drop_address(a, block_interval, "Re-drop by catch-and-release"); + } + } + +event new_connection(c: connection) + { + # let's only check originating connections... + check_conn(c$id$orig_h); + } diff --git a/testing/btest/Baseline/scripts.base.frameworks.pacf.catch-and-release/.stdout b/testing/btest/Baseline/scripts.base.frameworks.pacf.catch-and-release/.stdout new file mode 100644 index 0000000000..d7f55a9556 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.pacf.catch-and-release/.stdout @@ -0,0 +1,11 @@ +pacf debug (Debug-All): init +pacf debug (Debug-All): add_rule: [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::ADDRESS, conn=, flow=, ip=10.10.1.4/32, mac=], expire=10.0 mins, priority=0, location=, c=, i=, d=, s=, mod=, id=2, cid=2, _plugin_id=1] +pacf debug (Debug-All): add_rule: [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::ADDRESS, conn=, flow=, ip=10.10.1.4/32, mac=], expire=1.0 hr, priority=0, location=Re-drop by catch-and-release, c=, i=, d=, s=, mod=, id=3, cid=3, _plugin_id=1] +pacf debug (Debug-All): add_rule: [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::ADDRESS, conn=, flow=, ip=10.10.1.4/32, mac=], expire=1.0 day, priority=0, location=Re-drop by catch-and-release, c=, i=, d=, s=, mod=, id=4, cid=4, _plugin_id=1] +pacf debug (Debug-All): add_rule: [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::ADDRESS, conn=, flow=, ip=10.10.1.4/32, mac=], expire=7.0 days, priority=0, location=Re-drop by catch-and-release, c=, i=, d=, s=, mod=, id=5, cid=5, _plugin_id=1] +pacf debug (Debug-All): add_rule: [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::ADDRESS, conn=, flow=, ip=10.10.1.4/32, mac=], expire=7.0 days, priority=0, location=Re-drop by catch-and-release, c=, i=, d=, s=, mod=, id=6, cid=6, _plugin_id=1] +pacf debug (Debug-All): remove_rule: [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::ADDRESS, conn=, flow=, ip=10.10.1.4/32, mac=], expire=10.0 mins, priority=0, location=, c=, i=, d=, s=, mod=, id=2, cid=2, _plugin_id=1] +pacf debug (Debug-All): remove_rule: [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::ADDRESS, conn=, flow=, ip=10.10.1.4/32, mac=], expire=1.0 hr, priority=0, location=Re-drop by catch-and-release, c=, i=, d=, s=, mod=, id=3, cid=3, _plugin_id=1] +pacf debug (Debug-All): remove_rule: [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::ADDRESS, conn=, flow=, ip=10.10.1.4/32, mac=], expire=1.0 day, priority=0, location=Re-drop by catch-and-release, c=, i=, d=, s=, mod=, id=4, cid=4, _plugin_id=1] +pacf debug (Debug-All): remove_rule: [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::ADDRESS, conn=, flow=, ip=10.10.1.4/32, mac=], expire=7.0 days, priority=0, location=Re-drop by catch-and-release, c=, i=, d=, s=, mod=, id=6, cid=6, _plugin_id=1] +pacf debug (Debug-All): remove_rule: [ty=Pacf::DROP, target=Pacf::FORWARD, entity=[ty=Pacf::ADDRESS, conn=, flow=, ip=10.10.1.4/32, mac=], expire=7.0 days, priority=0, location=Re-drop by catch-and-release, c=, i=, d=, s=, mod=, id=5, cid=5, _plugin_id=1] diff --git a/testing/btest/Baseline/scripts.base.frameworks.pacf.catch-and-release/pacf.log b/testing/btest/Baseline/scripts.base.frameworks.pacf.catch-and-release/pacf.log new file mode 100644 index 0000000000..31a7c7dff4 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.pacf.catch-and-release/pacf.log @@ -0,0 +1,30 @@ +#separator \x09 +#set_separator , +#empty_field (empty) +#unset_field - +#path pacf +#open 2015-06-02-22-02-42 +#fields ts category cmd state action target entity_type entity msg location plugin +#types time enum string enum string enum string string string string string +0.000000 Pacf::MESSAGE - - - - - - activated plugin with priority 0 - Debug-All +1254722767.875996 Pacf::RULE ADD Pacf::REQUESTED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - (empty) Debug-All +1254722767.875996 Pacf::RULE ADD Pacf::REQUESTED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722767.875996 Pacf::RULE ADD Pacf::REQUESTED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722767.875996 Pacf::RULE ADD Pacf::REQUESTED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722767.875996 Pacf::RULE ADD Pacf::REQUESTED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722767.875996 Pacf::RULE ADD Pacf::SUCCEEDED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - (empty) Debug-All +1254722767.875996 Pacf::RULE ADD Pacf::SUCCEEDED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722767.875996 Pacf::RULE ADD Pacf::SUCCEEDED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722767.875996 Pacf::RULE ADD Pacf::SUCCEEDED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722767.875996 Pacf::RULE ADD Pacf::SUCCEEDED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722776.690444 Pacf::RULE REMOVE Pacf::REQUESTED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - (empty) Debug-All +1254722776.690444 Pacf::RULE REMOVE Pacf::REQUESTED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722776.690444 Pacf::RULE REMOVE Pacf::REQUESTED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722776.690444 Pacf::RULE REMOVE Pacf::REQUESTED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722776.690444 Pacf::RULE REMOVE Pacf::REQUESTED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722776.690444 Pacf::RULE REMOVE Pacf::SUCCEEDED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - (empty) Debug-All +1254722776.690444 Pacf::RULE REMOVE Pacf::SUCCEEDED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722776.690444 Pacf::RULE REMOVE Pacf::SUCCEEDED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722776.690444 Pacf::RULE REMOVE Pacf::SUCCEEDED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +1254722776.690444 Pacf::RULE REMOVE Pacf::SUCCEEDED Pacf::DROP Pacf::FORWARD Pacf::ADDRESS 10.10.1.4/32 - Re-drop by catch-and-release Debug-All +#close 2015-06-02-22-02-42 diff --git a/testing/btest/scripts/base/frameworks/pacf/basic.bro b/testing/btest/scripts/base/frameworks/pacf/basic.bro index 45ed9e65cc..0684daaecd 100644 --- a/testing/btest/scripts/base/frameworks/pacf/basic.bro +++ b/testing/btest/scripts/base/frameworks/pacf/basic.bro @@ -1,6 +1,6 @@ # @TEST-EXEC: bro -r $TRACES/smtp.trace %INPUT -# @TEST-EXEC: btest-diff pacf.log -# @TEST-EXEC: btest-diff .stdout +# @TEST-EXEC: TEST_DIFF_CANONIFIER='grep -v ^# | $SCRIPTS/diff-sort' btest-diff pacf.log +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-sort btest-diff .stdout @load base/frameworks/pacf diff --git a/testing/btest/scripts/base/frameworks/pacf/catch-and-release.bro b/testing/btest/scripts/base/frameworks/pacf/catch-and-release.bro new file mode 100644 index 0000000000..df6850ac3d --- /dev/null +++ b/testing/btest/scripts/base/frameworks/pacf/catch-and-release.bro @@ -0,0 +1,32 @@ +# @TEST-EXEC: bro -r $TRACES/smtp.trace %INPUT +# @TEST-EXEC: TEST_DIFF_CANONIFIER='grep -v ^# | $SCRIPTS/diff-sort' btest-diff pacf.log +# @TEST-EXEC: TEST_DIFF_CANONIFIER=$SCRIPTS/diff-sort btest-diff .stdout + +@load base/frameworks/pacf + +event bro_init() + { + local pacf_debug = Pacf::create_debug(T); + Pacf::activate(pacf_debug, 0); + } + +module Pacf; + +event connection_established(c: connection) + { + local id = c$id; + Pacf::drop_address_catch_release(id$orig_h); + # second one should be ignored because duplicate + Pacf::drop_address_catch_release(id$orig_h); + + # mean call directly into framework - simulate new connection + delete current_blocks[id$orig_h]; + check_conn(id$orig_h); + delete current_blocks[id$orig_h]; + check_conn(id$orig_h); + delete current_blocks[id$orig_h]; + check_conn(id$orig_h); + delete current_blocks[id$orig_h]; + check_conn(id$orig_h); + } + diff --git a/testing/btest/scripts/base/frameworks/pacf/multiple.bro b/testing/btest/scripts/base/frameworks/pacf/multiple.bro index eda66b1a93..baa8c5822e 100644 --- a/testing/btest/scripts/base/frameworks/pacf/multiple.bro +++ b/testing/btest/scripts/base/frameworks/pacf/multiple.bro @@ -1,5 +1,5 @@ # @TEST-EXEC: bro -r $TRACES/smtp.trace %INPUT -# @TEST-EXEC: btest-diff pacf.log +# @TEST-EXEC: TEST_DIFF_CANONIFIER='grep -v ^# | $SCRIPTS/diff-sort' btest-diff pacf.log @load base/frameworks/pacf