diff --git a/scripts/base/frameworks/netcontrol/main.bro b/scripts/base/frameworks/netcontrol/main.bro index 92e33175e7..580d9b6d30 100644 --- a/scripts/base/frameworks/netcontrol/main.bro +++ b/scripts/base/frameworks/netcontrol/main.bro @@ -126,6 +126,20 @@ export { ## asynchronously and thus go wrong at that point. global remove_rule: function(id: string) : bool; + ## Searches all rules affecting a certain IP address + ## + ## ip: The ip address to search for + ## + ## Returns: vector of all rules affecting the IP address + global find_rules_addr: function(ip: addr) : vector of Rule; + + ## Searches all rules affecting a certain subnet + ## + ## sn: The subnet to search for + ## + ## Returns: vector of all rules affecting the subnet + global find_rules_subnet: function(sn: subnet) : vector of Rule; + ###### Asynchronous feedback on rules. ## Confirms that a rule was put in place. @@ -271,8 +285,7 @@ global plugin_ids: table[count] of PluginState; global rules: table[string] of Rule; # Rules indexed by id and cid # All rules that apply to a certain subnet/IP address. -# Contains only succesfully added rules. -global rules_by_subnets: table[subnet] of set[Rule]; +global rules_by_subnets: table[subnet] of set[string]; # Rules pertaining to a specific entity. # There always only can be one rule of each type for one entity. @@ -563,6 +576,96 @@ function activate_impl(p: PluginState, priority: int) } +function add_one_subnet_entry(s: subnet, r: Rule) + { + if ( ! check_subnet(s, rules_by_subnets) ) + rules_by_subnets[s] = set(r$id); + else + add rules_by_subnets[s][r$id]; + } + +function add_subnet_entry(rule: Rule) + { + local e = rule$entity; + if ( e$ty == ADDRESS ) + { + add_one_subnet_entry(e$ip, rule); + } + else if ( e$ty == CONNECTION ) + { + add_one_subnet_entry(addr_to_subnet(e$conn$orig_h), rule); + add_one_subnet_entry(addr_to_subnet(e$conn$resp_h), rule); + } + else if ( e$ty == FLOW ) + { + if ( e$flow?$src_h ) + add_one_subnet_entry(e$flow$src_h, rule); + if ( e$flow?$dst_h ) + add_one_subnet_entry(e$flow$dst_h, rule); + } + } + +function remove_one_subnet_entry(s: subnet, r: Rule) + { + if ( ! check_subnet(s, rules_by_subnets) ) + return; + + if ( r$id !in rules_by_subnets[s] ) + return; + + delete rules_by_subnets[s][r$id]; + if ( |rules_by_subnets[s]| == 0 ) + delete rules_by_subnets[s]; + } + +function remove_subnet_entry(rule: Rule) + { + local e = rule$entity; + if ( e$ty == ADDRESS ) + { + remove_one_subnet_entry(e$ip, rule); + } + else if ( e$ty == CONNECTION ) + { + remove_one_subnet_entry(addr_to_subnet(e$conn$orig_h), rule); + remove_one_subnet_entry(addr_to_subnet(e$conn$resp_h), rule); + } + else if ( e$ty == FLOW ) + { + if ( e$flow?$src_h ) + remove_one_subnet_entry(e$flow$src_h, rule); + if ( e$flow?$dst_h ) + remove_one_subnet_entry(e$flow$dst_h, rule); + } + } + +function find_rules_subnet(sn: subnet) : vector of Rule + { + local ret: vector of Rule = vector(); + + local matches = matching_subnets(sn, rules_by_subnets); + + for ( m in matches ) + { + local sn_entry = matches[m]; + local rule_ids = rules_by_subnets[sn_entry]; + for ( rule_id in rules_by_subnets[sn_entry] ) + { + if ( rule_id in rules ) + ret[|ret|] = rules[rule_id]; + else + Reporter::error("find_rules_subnet - internal data structure error, missing rule"); + } + } + + return ret; + } + +function find_rules_addr(ip: addr) : vector of Rule + { + return find_rules_subnet(addr_to_subnet(ip)); + } + function add_rule_impl(rule: Rule) : string { if ( ! plugins_active ) @@ -614,6 +717,9 @@ function add_rule_impl(rule: Rule) : string { rules[rule$id] = rule; rule_entities[rule$entity, rule$ty] = rule; + + add_subnet_entry(rule); + return rule$id; } @@ -703,6 +809,8 @@ 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]; } diff --git a/testing/btest/Baseline/scripts.base.frameworks.netcontrol.delete-internal-state/.stdout b/testing/btest/Baseline/scripts.base.frameworks.netcontrol.delete-internal-state/.stdout new file mode 100644 index 0000000000..7d21c082f7 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.netcontrol.delete-internal-state/.stdout @@ -0,0 +1,19 @@ +netcontrol debug (Debug-All): init +netcontrol debug (Debug-All): add_rule: [ty=NetControl::DROP, target=NetControl::MONITOR, entity=[ty=NetControl::FLOW, conn=, flow=[src_h=192.168.18.50/32, src_p=56981/tcp, dst_h=74.125.239.97/32, dst_p=443/tcp, src_m=, dst_m=], ip=, mac=], expire=0 secs, priority=0, location=, out_port=, mod=, id=2, cid=2, _plugin_ids={\x0a\x0a}, _active_plugin_ids={\x0a\x0a}, _added=F] +netcontrol debug (Debug-All): add_rule: [ty=NetControl::DROP, target=NetControl::FORWARD, entity=[ty=NetControl::ADDRESS, conn=, flow=, ip=192.168.18.50/32, mac=], expire=0 secs, priority=0, location=, out_port=, mod=, id=3, cid=3, _plugin_ids={\x0a\x0a}, _active_plugin_ids={\x0a\x0a}, _added=F] +netcontrol debug (Debug-All): add_rule: [ty=NetControl::WHITELIST, target=NetControl::FORWARD, entity=[ty=NetControl::ADDRESS, conn=, flow=, ip=192.168.18.50/32, mac=], expire=0 secs, priority=5, location=, out_port=, mod=, id=4, cid=4, _plugin_ids={\x0a\x0a}, _active_plugin_ids={\x0a\x0a}, _added=F] +netcontrol debug (Debug-All): add_rule: [ty=NetControl::REDIRECT, target=NetControl::FORWARD, entity=[ty=NetControl::FLOW, conn=, flow=[src_h=192.168.18.50/32, src_p=56981/tcp, dst_h=74.125.239.97/32, dst_p=443/tcp, src_m=, dst_m=], ip=, mac=], expire=0 secs, priority=0, location=, out_port=5, mod=, id=5, cid=5, _plugin_ids={\x0a\x0a}, _active_plugin_ids={\x0a\x0a}, _added=F] +netcontrol debug (Debug-All): remove_rule: [ty=NetControl::DROP, target=NetControl::MONITOR, entity=[ty=NetControl::FLOW, conn=, flow=[src_h=192.168.18.50/32, src_p=56981/tcp, dst_h=74.125.239.97/32, dst_p=443/tcp, src_m=, dst_m=], ip=, mac=], expire=0 secs, priority=0, location=, out_port=, mod=, id=2, cid=2, _plugin_ids={\x0a\x091\x0a}, _active_plugin_ids={\x0a\x091\x0a}, _added=T] +netcontrol debug (Debug-All): remove_rule: [ty=NetControl::DROP, target=NetControl::FORWARD, entity=[ty=NetControl::ADDRESS, conn=, flow=, ip=192.168.18.50/32, mac=], expire=0 secs, priority=0, location=, out_port=, mod=, id=3, cid=3, _plugin_ids={\x0a\x091\x0a}, _active_plugin_ids={\x0a\x091\x0a}, _added=T] +netcontrol debug (Debug-All): remove_rule: [ty=NetControl::WHITELIST, target=NetControl::FORWARD, entity=[ty=NetControl::ADDRESS, conn=, flow=, ip=192.168.18.50/32, mac=], expire=0 secs, priority=5, location=, out_port=, mod=, id=4, cid=4, _plugin_ids={\x0a\x091\x0a}, _active_plugin_ids={\x0a\x091\x0a}, _added=T] +netcontrol debug (Debug-All): remove_rule: [ty=NetControl::REDIRECT, target=NetControl::FORWARD, entity=[ty=NetControl::FLOW, conn=, flow=[src_h=192.168.18.50/32, src_p=56981/tcp, dst_h=74.125.239.97/32, dst_p=443/tcp, src_m=, dst_m=], ip=, mac=], expire=0 secs, priority=0, location=, out_port=5, mod=, id=5, cid=5, _plugin_ids={\x0a\x091\x0a}, _active_plugin_ids={\x0a\x091\x0a}, _added=T] +Dumping state +{ + +} +{ + +} +{ + +} diff --git a/testing/btest/Baseline/scripts.base.frameworks.netcontrol.find-rules/out b/testing/btest/Baseline/scripts.base.frameworks.netcontrol.find-rules/out new file mode 100644 index 0000000000..b29ce14d29 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.frameworks.netcontrol.find-rules/out @@ -0,0 +1,6 @@ +1 +[ty=NetControl::ADDRESS, conn=, flow=, ip=1.2.3.4/32, mac=] +0 +4 +[ty=NetControl::FLOW, conn=, flow=[src_h=127.0.0.2/32, src_p=, dst_h=8.8.8.8/32, dst_p=53/udp, src_m=, dst_m=], ip=, mac=], NetControl::MODIFY +[ty=NetControl::FLOW, conn=, flow=[src_h=127.0.0.2/32, src_p=, dst_h=, dst_p=, src_m=, dst_m=], ip=, mac=], NetControl::DROP diff --git a/testing/btest/scripts/base/frameworks/netcontrol/delete-internal-state.bro b/testing/btest/scripts/base/frameworks/netcontrol/delete-internal-state.bro new file mode 100644 index 0000000000..9b8c995fac --- /dev/null +++ b/testing/btest/scripts/base/frameworks/netcontrol/delete-internal-state.bro @@ -0,0 +1,54 @@ +# @TEST-EXEC: bro -r $TRACES/tls/ecdhe.pcap %INPUT +# @TEST-EXEC: btest-diff .stdout + +# Verify the state of internal tables after rules have been deleted... + +@load base/frameworks/netcontrol + +module NetControl; + +export { + global dump_state: function(); +} + +function dump_state() + { + print "Dumping state"; + print rules; + print rule_entities; + print rules_by_subnets; + } + +module GLOBAL; + +global rules: vector of string; + +event NetControl::init() + { + local netcontrol_debug = NetControl::create_debug(T); + NetControl::activate(netcontrol_debug, 10); + } + +event remove_all() + { + for ( i in rules ) + NetControl::remove_rule(rules[i]); + } + +event dump_info() + { + NetControl::dump_state(); + } + +event connection_established(c: connection) + { + local id = c$id; + rules[|rules|] = NetControl::shunt_flow([$src_h=id$orig_h, $src_p=id$orig_p, $dst_h=id$resp_h, $dst_p=id$resp_p], 0secs); + rules[|rules|] = NetControl::drop_address(id$orig_h, 0secs); + rules[|rules|] = NetControl::whitelist_address(id$orig_h, 0secs); + rules[|rules|] = NetControl::redirect_flow([$src_h=id$orig_h, $src_p=id$orig_p, $dst_h=id$resp_h, $dst_p=id$resp_p], 5, 0secs); + + schedule 1sec { remove_all() }; + schedule 2sec { dump_info() }; + } + diff --git a/testing/btest/scripts/base/frameworks/netcontrol/find-rules.bro b/testing/btest/scripts/base/frameworks/netcontrol/find-rules.bro new file mode 100644 index 0000000000..e7bb61cc04 --- /dev/null +++ b/testing/btest/scripts/base/frameworks/netcontrol/find-rules.bro @@ -0,0 +1,34 @@ +# @TEST-EXEC: bro %INPUT +# @TEST-EXEC: btest-diff out + +@load base/frameworks/netcontrol + +global outfile: file; + +event NetControl::init() + { + local netcontrol_debug = NetControl::create_debug(T); + NetControl::activate(netcontrol_debug, 0); + } + +event NetControl::init_done() &priority=-5 + { + NetControl::shunt_flow([$src_h=192.168.17.1, $src_p=32/tcp, $dst_h=192.168.17.2, $dst_p=32/tcp], 30sec); + NetControl::drop_address(1.1.2.2, 15sec, "Hi there"); + NetControl::whitelist_address(1.2.3.4, 15sec); + NetControl::redirect_flow([$src_h=192.168.17.1, $src_p=32/tcp, $dst_h=192.168.17.2, $dst_p=32/tcp], 5, 30sec); + NetControl::quarantine_host(127.0.0.2, 8.8.8.8, 127.0.0.3, 15sec); + + outfile = open("out"); + local rules = NetControl::find_rules_addr(1.2.3.4); + print outfile, |rules|; + print outfile, rules[0]$entity; + rules = NetControl::find_rules_addr(1.2.3.5); + print outfile, |rules|; + rules = NetControl::find_rules_addr(127.0.0.2); + print outfile, |rules|; + print outfile, rules[0]$entity, rules[0]$ty; + print outfile, rules[3]$entity, rules[3]$ty; + close(outfile); + } +