mirror of
https://github.com/zeek/zeek.git
synced 2025-10-03 07:08:19 +00:00
Updates for the PacketFilter framework to simplify it.
This commit is contained in:
parent
5f8ee93ef0
commit
4149724f59
16 changed files with 64 additions and 144 deletions
|
@ -10,6 +10,8 @@
|
|||
##! the analyzers themselves, and documented in their analyzer-specific
|
||||
##! description along with the events that they generate.
|
||||
|
||||
@load base/frameworks/packet-filter/utils
|
||||
|
||||
module Analyzer;
|
||||
|
||||
export {
|
||||
|
@ -96,7 +98,21 @@ export {
|
|||
##
|
||||
## Returns: True if succesful.
|
||||
global schedule_analyzer: function(orig: addr, resp: addr, resp_p: port,
|
||||
analyzer: Analyzer::Tag, tout: interval) : bool;
|
||||
analyzer: Analyzer::Tag, tout: interval) : bool;
|
||||
|
||||
## Automatically creates a BPF filter for the specified protocol based
|
||||
## on the data supplied for the protocol through the
|
||||
## :bro:see:`Analyzer::register_for_ports` function.
|
||||
##
|
||||
## tag: The analyzer tag.
|
||||
##
|
||||
## Returns: BPF filter string.
|
||||
global analyzer_to_bpf: function(tag: Analyzer::Tag): string;
|
||||
|
||||
## Create a BPF filter which matches all of the ports defined
|
||||
## by the various protocol analysis scripts as "registered ports"
|
||||
## for the protocol.
|
||||
global get_bpf: function(): string;
|
||||
|
||||
## A set of analyzers to disable by default at startup. The default set
|
||||
## contains legacy analyzers that are no longer supported.
|
||||
|
@ -177,3 +193,25 @@ function schedule_analyzer(orig: addr, resp: addr, resp_p: port,
|
|||
return __schedule_analyzer(orig, resp, resp_p, analyzer, tout);
|
||||
}
|
||||
|
||||
function analyzer_to_bpf(tag: Analyzer::Tag): string
|
||||
{
|
||||
# Return an empty string if an undefined analyzer was given.
|
||||
if ( tag !in ports )
|
||||
return "";
|
||||
|
||||
local output = "";
|
||||
for ( p in ports[tag] )
|
||||
output = PacketFilter::combine_filters(output, "or", PacketFilter::port_to_bpf(p));
|
||||
return output;
|
||||
}
|
||||
|
||||
function get_bpf(): string
|
||||
{
|
||||
local output = "";
|
||||
for ( tag in ports )
|
||||
{
|
||||
output = PacketFilter::combine_filters(output, "or", analyzer_to_bpf(tag));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
@load ./utils
|
||||
@load ./main
|
||||
@load ./shunt
|
||||
@load ./netstats
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
##! :bro:id:`capture_filters` and :bro:id:`restrict_filters` variables.
|
||||
|
||||
@load base/frameworks/notice
|
||||
@load base/frameworks/protocols
|
||||
@load base/frameworks/analyzer
|
||||
@load ./utils
|
||||
|
||||
module PacketFilter;
|
||||
|
@ -64,13 +64,13 @@ export {
|
|||
## The maximum amount of time that you'd like to allow for BPF filters to compile.
|
||||
## If this time is exceeded, compensation measures may be taken by the framework
|
||||
## to reduce the filter size. This threshold being crossed also results in
|
||||
## the :bro:enum:`PacketFilter::Too_Long_To_Compile_Filter` notice.
|
||||
## the :bro:see:`PacketFilter::Too_Long_To_Compile_Filter` notice.
|
||||
const max_filter_compile_time = 100msec &redef;
|
||||
|
||||
## Install a BPF filter to exclude some traffic. The filter should positively
|
||||
## match what is to be excluded, it will be wrapped in a "not".
|
||||
##
|
||||
## filter_id: A somewhat arbitrary string that can be used to identify
|
||||
## filter_id: An arbitrary string that can be used to identify
|
||||
## the filter.
|
||||
##
|
||||
## filter: A BPF expression of traffic that should be excluded.
|
||||
|
@ -83,7 +83,7 @@ export {
|
|||
## the BPF filter. The filter should match the traffic you don't want
|
||||
## to see (it will be wrapped in a "not" condition).
|
||||
##
|
||||
## filter_id: A somewhat arbitrary string that can be used to identify
|
||||
## filter_id: An arbitrary string that can be used to identify
|
||||
## the filter.
|
||||
##
|
||||
## filter: A BPF expression of traffic that should be excluded.
|
||||
|
@ -119,11 +119,8 @@ export {
|
|||
|
||||
global dynamic_restrict_filters: table[string] of string = {};
|
||||
|
||||
# Set the default capture filter.
|
||||
redef capture_filters += { ["default"] = default_capture_filter };
|
||||
|
||||
# Track if a filter is currenlty building so functions that would ultimately
|
||||
# install a filter immediately can still be used buy they won't try to build or
|
||||
# Track if a filter is currently building so functions that would ultimately
|
||||
# install a filter immediately can still be used but they won't try to build or
|
||||
# install the filter.
|
||||
global currently_building = F;
|
||||
|
||||
|
@ -239,7 +236,7 @@ function build(): string
|
|||
cfilter = combine_filters(cfilter, "or", capture_filters[id]);
|
||||
|
||||
if ( enable_auto_protocol_capture_filters )
|
||||
cfilter = combine_filters(cfilter, "or", Protocols::to_bpf());
|
||||
cfilter = combine_filters(cfilter, "or", Analyzer::get_bpf());
|
||||
|
||||
# Apply the restriction filters.
|
||||
local rfilter = "";
|
||||
|
@ -269,6 +266,10 @@ function install(): bool
|
|||
|
||||
local tmp_filter = build();
|
||||
|
||||
# No need to proceed if the filter hasn't changed.
|
||||
if ( tmp_filter == current_filter )
|
||||
return F;
|
||||
|
||||
local ts = current_time();
|
||||
if ( ! precompile_pcap_filter(DefaultPcapFilter, tmp_filter) )
|
||||
{
|
||||
|
@ -283,8 +284,8 @@ function install(): bool
|
|||
local diff = current_time()-ts;
|
||||
if ( diff > max_filter_compile_time )
|
||||
NOTICE([$note=Too_Long_To_Compile_Filter,
|
||||
$msg=fmt("A BPF filter is taking longer than %0.6f seconds to compile", diff)]);
|
||||
|
||||
$msg=fmt("A BPF filter is taking longer than %0.1f seconds to compile", diff)]);
|
||||
|
||||
# Set it to the current filter if it passed precompiling
|
||||
current_filter = tmp_filter;
|
||||
|
||||
|
|
|
@ -1,170 +0,0 @@
|
|||
@load base/frameworks/notice
|
||||
@load ./main
|
||||
@load ./utils
|
||||
|
||||
module PacketFilter;
|
||||
|
||||
export {
|
||||
## The maximum number of BPF based shunts that Bro is allowed to perform.
|
||||
const max_bpf_shunts = 100 &redef;
|
||||
|
||||
## Call this function to use BPF to shunt a connection (to prevent the
|
||||
## data packets from reaching Bro). For TCP connections, control packets
|
||||
## are still allowed through so that Bro can continue logging the connection
|
||||
## and it can stop shunting once the connection ends.
|
||||
global shunt_conn: function(id: conn_id): bool;
|
||||
|
||||
## This function will use a BPF expresssion to shunt traffic between
|
||||
## the two hosts given in the `conn_id` so that the traffic is never
|
||||
## exposed to Bro's traffic processing.
|
||||
global shunt_host_pair: function(id: conn_id): bool;
|
||||
|
||||
## Remove shunting for a host pair given as a `conn_id`. The filter
|
||||
## is not immediately removed. It waits for the occassional filter
|
||||
## update done by the `PacketFilter` framework.
|
||||
global unshunt_host_pair: function(id: conn_id): bool;
|
||||
|
||||
## Performs the same function as the `unshunt_host_pair` function, but
|
||||
## it forces an immediate filter update.
|
||||
global force_unshunt_host_pair: function(id: conn_id): bool;
|
||||
|
||||
## Retrieve the currently shunted connections.
|
||||
global current_shunted_conns: function(): set[conn_id];
|
||||
|
||||
## Retrieve the currently shunted host pairs.
|
||||
global current_shunted_host_pairs: function(): set[conn_id];
|
||||
|
||||
redef enum Notice::Type += {
|
||||
## Indicative that :bro:id:`max_bpf_shunts` connections are already
|
||||
## being shunted with BPF filters and no more are allowed.
|
||||
No_More_Conn_Shunts_Available,
|
||||
|
||||
## Limitations in BPF make shunting some connections with BPF impossible.
|
||||
## This notice encompasses those various cases.
|
||||
Cannot_BPF_Shunt_Conn,
|
||||
};
|
||||
}
|
||||
|
||||
global shunted_conns: set[conn_id];
|
||||
global shunted_host_pairs: set[conn_id];
|
||||
|
||||
function shunt_filters()
|
||||
{
|
||||
# NOTE: this could wrongly match if a connection happens with the ports reversed.
|
||||
local tcp_filter = "";
|
||||
local udp_filter = "";
|
||||
for ( id in shunted_conns )
|
||||
{
|
||||
local prot = get_port_transport_proto(id$resp_p);
|
||||
|
||||
local filt = fmt("host %s and port %d and host %s and port %d", id$orig_h, id$orig_p, id$resp_h, id$resp_p);
|
||||
if ( prot == udp )
|
||||
udp_filter = combine_filters(udp_filter, "and", filt);
|
||||
else if ( prot == tcp )
|
||||
tcp_filter = combine_filters(tcp_filter, "and", filt);
|
||||
}
|
||||
if ( tcp_filter != "" )
|
||||
tcp_filter = combine_filters("tcp and tcp[tcpflags] & (tcp-syn|tcp-fin|tcp-rst) == 0", "and", tcp_filter);
|
||||
local conn_shunt_filter = combine_filters(tcp_filter, "and", udp_filter);
|
||||
|
||||
local hp_shunt_filter = "";
|
||||
for ( id in shunted_host_pairs )
|
||||
hp_shunt_filter = combine_filters(hp_shunt_filter, "and", fmt("host %s and host %s", id$orig_h, id$resp_h));
|
||||
|
||||
local filter = combine_filters(conn_shunt_filter, "and", hp_shunt_filter);
|
||||
if ( filter != "" )
|
||||
PacketFilter::exclude("shunt_filters", filter);
|
||||
}
|
||||
|
||||
event bro_init() &priority=5
|
||||
{
|
||||
register_filter_plugin([
|
||||
$func()={ return shunt_filters(); }
|
||||
]);
|
||||
}
|
||||
|
||||
function current_shunted_conns(): set[conn_id]
|
||||
{
|
||||
return shunted_conns;
|
||||
}
|
||||
|
||||
function current_shunted_host_pairs(): set[conn_id]
|
||||
{
|
||||
return shunted_host_pairs;
|
||||
}
|
||||
|
||||
function reached_max_shunts(): bool
|
||||
{
|
||||
if ( |shunted_conns| + |shunted_host_pairs| > max_bpf_shunts )
|
||||
{
|
||||
NOTICE([$note=No_More_Conn_Shunts_Available,
|
||||
$msg=fmt("%d BPF shunts are in place and no more will be added until space clears.", max_bpf_shunts)]);
|
||||
return T;
|
||||
}
|
||||
else
|
||||
return F;
|
||||
}
|
||||
|
||||
function shunt_host_pair(id: conn_id): bool
|
||||
{
|
||||
PacketFilter::filter_changed = T;
|
||||
|
||||
if ( reached_max_shunts() )
|
||||
return F;
|
||||
|
||||
add shunted_host_pairs[id];
|
||||
install();
|
||||
return T;
|
||||
}
|
||||
|
||||
function unshunt_host_pair(id: conn_id): bool
|
||||
{
|
||||
PacketFilter::filter_changed = T;
|
||||
|
||||
if ( id in shunted_host_pairs )
|
||||
{
|
||||
delete shunted_host_pairs[id];
|
||||
return T;
|
||||
}
|
||||
else
|
||||
return F;
|
||||
}
|
||||
|
||||
function force_unshunt_host_pair(id: conn_id): bool
|
||||
{
|
||||
if ( unshunt_host_pair(id) )
|
||||
{
|
||||
install();
|
||||
return T;
|
||||
}
|
||||
else
|
||||
return F;
|
||||
}
|
||||
|
||||
function shunt_conn(id: conn_id): bool
|
||||
{
|
||||
if ( is_v6_addr(id$orig_h) )
|
||||
{
|
||||
NOTICE([$note=Cannot_BPF_Shunt_Conn,
|
||||
$msg="IPv6 connections can't be shunted with BPF due to limitations in BPF",
|
||||
$sub="ipv6_conn",
|
||||
$id=id, $identifier=cat(id)]);
|
||||
return F;
|
||||
}
|
||||
|
||||
if ( reached_max_shunts() )
|
||||
return F;
|
||||
|
||||
PacketFilter::filter_changed = T;
|
||||
add shunted_conns[id];
|
||||
install();
|
||||
return T;
|
||||
}
|
||||
|
||||
event connection_state_remove(c: connection) &priority=-5
|
||||
{
|
||||
# Don't rebuild the filter right away because the packet filter framework
|
||||
# will check every few minutes and update the filter if things have changed.
|
||||
if ( c$id in shunted_conns )
|
||||
delete shunted_conns[c$id];
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
@load ./main
|
|
@ -1,59 +0,0 @@
|
|||
|
||||
@load base/frameworks/packet-filter/utils
|
||||
|
||||
module Protocols;
|
||||
|
||||
export {
|
||||
|
||||
const common_ports: table[string] of set[port] = {} &redef;
|
||||
|
||||
## Automatically creates a BPF filter for the specified protocol based
|
||||
## on the data supplied for the protocol in the :bro:id:`common_ports`
|
||||
## variable.
|
||||
##
|
||||
## protocol: A string representation for a protocol, e.g. "HTTP"
|
||||
##
|
||||
## Returns: BPF filter string.
|
||||
global protocol_to_bpf: function(protocol: string): string;
|
||||
|
||||
## Create a BPF filter which matches all of the ports defined
|
||||
## by the various protocol analysis scripts as "common ports"
|
||||
## for the protocol.
|
||||
global to_bpf: function(): string;
|
||||
|
||||
## Maps between human readable protocol identifiers (like "HTTP")
|
||||
## and the internal Bro representation for an analyzer (like ANALYZER_HTTP).
|
||||
## This is typically fully populated by the base protocol analyzer scripts.
|
||||
const analyzer_map: table[string] of set[AnalyzerTag] = {} &redef;
|
||||
}
|
||||
|
||||
event bro_init() &priority=10
|
||||
{
|
||||
for ( proto in common_ports )
|
||||
{
|
||||
for ( p in common_ports[proto] )
|
||||
dpd_analyzer_ports[p] = analyzer_map[proto];
|
||||
for ( a in analyzer_map[proto] )
|
||||
dpd_config[a] = [$ports=common_ports[proto]];
|
||||
}
|
||||
}
|
||||
|
||||
function protocol_to_bpf(protocol: string): string
|
||||
{
|
||||
# Return an empty string if an undefined protocol was given.
|
||||
if ( protocol !in common_ports )
|
||||
return "";
|
||||
|
||||
local output = "";
|
||||
for ( one_port in common_ports[protocol] )
|
||||
output = PacketFilter::combine_filters(output, "or", PacketFilter::port_to_bpf(one_port));
|
||||
return output;
|
||||
}
|
||||
|
||||
function to_bpf(): string
|
||||
{
|
||||
local output = "";
|
||||
for ( p in common_ports )
|
||||
output = PacketFilter::combine_filters(output, "or", protocol_to_bpf(p));
|
||||
return output;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue