zeek/policy/remote.bro

263 lines
6 KiB
Text

# $Id: remote.bro 5101 2007-11-29 07:02:27Z vern $
#
# Connect to remote Bros and request some of their events.
module Remote;
export {
const default_port_ssl = 47756/tcp &redef;
const default_port_clear = 47757/tcp &redef;
# Default compression level.
global default_compression = 0 &redef;
# A remote peer to which we would like to talk.
# If there's no entry for a peer, it may still connect
# and request state, but not send us any.
type Destination : record {
# Destination endpoint.
host: addr;
p: port &optional;
# When accepting a connection, the configuration only
# applies if the class matches the one transmitted by
# the peer.
#
# When initiating a connection, the class is sent to
# the other side.
class: string &optional;
# Events requested from remote side.
events: pattern &optional;
# Whether we are going to connect (rather than waiting
# for the other sie to connect to us).
connect: bool &default = F;
# If disconnected, reconnect after this many seconds.
retry: interval &default = 0 secs;
# Whether to accept remote events.
accept_input: bool &default = T;
# Whether to perform state synchronization with peer.
sync: bool &default = T;
# When performing state synchronization, whether we consider
# our state to be authoritative. If so, we will send the peer
# our current set when the connection is set up.
# (Only one side can be authoritative.)
auth: bool &default = F;
# If not set, no capture filter is sent.
# If set to "", the default cature filter is sent.
capture_filter: string &optional;
# Whether to use SSL-based communication.
ssl: bool &default = F;
# Take-over state from this host
# (activated by loading hand-over.bro)
hand_over: bool &default = F;
# Compression level is 0-9, with 0 = no compression.
compression: count &default = default_compression;
# Set when connected.
peer: event_peer &optional;
connected: bool &default = F;
};
const destinations: table[string] of Destination &redef;
# redef destinations += {
# ["foo"] = [$host = foo.bar.com, $events = /.*/, $connect=T, $retry = 60 secs, $ssl=T]
# };
# Write log message into remote.log
global do_script_log: function(p: event_peer, msg: string);
global pending_peers: table[peer_id] of Destination;
global connected_peers: table[peer_id] of Destination;
# Connect to destionations[dst], independent of its "connect" flag.
global connect_peer: function(peer: string);
}
# Called rm_log rather than remote_log because there's an event by that name.
global rm_log = open_log_file("remote");
global src_names = {
[REMOTE_SRC_CHILD] = "[child] ",
[REMOTE_SRC_PARENT] = "[parent]",
[REMOTE_SRC_SCRIPT] = "[script]",
};
function do_script_log_common(level: count, src: count, msg: string)
{
print rm_log,
fmt("%.6f %s %s %s", current_time(),
(level == REMOTE_LOG_INFO ? "[info] " : "[error]"),
src_names[src], msg);
}
event remote_log(level: count, src: count, msg: string)
{
do_script_log_common(level, src, msg);
}
function do_script_log(p: event_peer, msg: string)
{
do_script_log_common(REMOTE_LOG_INFO, REMOTE_SRC_SCRIPT,
fmt("[#%d/%s:%d] %s", p$id, p$host, p$p, msg));
}
function connect_peer(peer: string)
{
local dst = destinations[peer];
local p = dst$ssl ? default_port_ssl : default_port_clear;
if ( dst?$p )
p = dst$p;
local class = dst?$class ? dst$class : "";
local id = connect(dst$host, p, class ,dst$retry, dst$ssl);
if ( id == PEER_ID_NONE )
print rm_log,
fmt("%.6f %s/%d can't trigger connect",
current_time(), dst$host, p);
pending_peers[id] = dst;
}
event bro_init() &priority = -10 # let others modify destinations
{
set_buf(rm_log, F);
for ( tag in destinations )
{
if ( ! destinations[tag]$connect )
next;
connect_peer(tag);
}
}
function setup_peer(p: event_peer, dst: Destination)
{
if ( dst?$events )
{
do_script_log(p, fmt("requesting events matching %s", dst$events));
request_remote_events(p, dst$events);
}
if ( dst?$capture_filter )
{
local filter = dst$capture_filter;
if ( filter == "" )
filter = default_pcap_filter;
do_script_log(p, fmt("sending capture_filter: %s", filter));
send_capture_filter(p, filter);
}
if ( dst$accept_input )
{
do_script_log(p, "accepting state");
set_accept_state(p, T);
}
set_compression_level(p, dst$compression);
if ( dst$sync )
{
do_script_log(p, "requesting synchronized state");
request_remote_sync(p, dst$auth);
}
dst$peer = p;
dst$connected = T;
connected_peers[p$id] = dst;
}
event remote_connection_established(p: event_peer)
{
if ( is_remote_event() )
return;
do_script_log(p, "connection established");
if ( p$id in pending_peers )
{
# We issued the connect.
local dst = pending_peers[p$id];
setup_peer(p, dst);
delete pending_peers[p$id];
}
else
{ # The other side connected to us.
local found = F;
for ( i in destinations )
{
dst = destinations[i];
if ( dst$host == p$host )
{
local c = 0;
# See if classes match = either both have
# the same class, or neither of them has
# a class.
if ( p?$class && p$class != "" )
++c;
if ( dst?$class && dst$class != "" )
++c;
if ( c == 1 ||
(c == 2 && p$class != dst$class) )
next;
found = T;
setup_peer(p, dst);
break;
}
}
if ( ! found )
set_compression_level(p, default_compression);
}
complete_handshake(p);
}
event remote_connection_closed(p: event_peer)
{
if ( is_remote_event() )
return;
do_script_log(p, "connection closed");
if ( p$id in connected_peers )
{
local dst = connected_peers[p$id];
dst$connected = F;
delete connected_peers[p$id];
if ( dst$retry != 0secs )
# The core will retry.
pending_peers[p$id] = dst;
}
}
event remote_state_inconsistency(operation: string, id: string,
expected_old: string, real_old: string)
{
if ( is_remote_event() )
return;
print rm_log,
fmt("%.6f state inconsistency: %s should be %s but is %s before %s",
network_time(), id, expected_old, real_old, operation);
}