Checkpoint for logging framework

This commit is contained in:
Seth Hall 2011-01-19 09:36:06 -05:00
parent aa0691ba21
commit f3b148b019
5 changed files with 139 additions and 1 deletions

93
policy/logging.bro Normal file
View file

@ -0,0 +1,93 @@
module Logging;
export {
# The set of writers Bro provides.
type Writer: enum {
WRITER_DEFAULT, # See default_writer below.
WRITER_CSV,
WRITER_DATA_SERIES,
WRITER_SYSLOG
};
# Each stream gets a unique ID. This type will be extended by
# other scripts.
type ID: enum {
Unknown
};
# The default writer to use if a filter does not specify
# anything else.
const default_writer = WRITER_CSV &redef;
# Type defining a stream.
type Stream: record {
id : ID; # The ID of the stream.
columns : any; # A record type defining the stream's output columns.
};
# A filter defining what to record.
type Filter: record {
# A name to reference this filter.
name: string;
# A predicate returning True if the filter wants a log entry
# to be recorded. If not given, an implicit True is assumed
# for all entries. The predicate receives one parameter:
# an instance of the log's record type with the fields to be
# logged.
pred: function(log: any) &optional;
# A path for outputting everything matching this
# filter. The path is either a string, or a function
# called with a single ``ID`` argument and returning a string.
#
# The specific interpretation of the string is left to the
# Writer, but if it's refering to a file, it's assumed that no
# extension is given; the writer will add whatever is
# appropiate.
path: any &optional;
# A subset of column names to record. If not given, all
# columns are recorded.
select: set[string] &optional;
# An event that is raised whenever the filter is applied
# to an entry. The event receives the same parameter
# as the predicate. It will always be generated,
# independent of what the predicate returns.
ev: event(c: connection, log: any) &optional;
# The writer to use.
writer: Writer &default=default_writer;
};
global filters: table[ID] of set[Filter];
# Logs the record "rec" to the stream "id". The type of
# "rec" must match the stream's "columns" field.
global log: function(id: ID, rec: any);
# Returns an existing filter previously installed for stream
# "id" under the given "name". If no such filter exists,
# the record "NoSuchFilter" is returned.
global get_filter: function(id: ID, name: string) : Filter;
global create: function(stream: Stream);
global add_filter: function(id: ID, filter: Filter);
}
# Sentinel representing an unknown filter.
const NoSuchFilter: Filter = [$name="<unknown filter>"];
function add_filter(id: ID, filter: Filter)
{
if ( id !in filters )
filters[id] = set();
add filters[id][filter];
}
function log(id: ID, rec: any)
{
logging_log(id, rec);
}

27
policy/test-logging.bro Normal file
View file

@ -0,0 +1,27 @@
module SSH;
@load logging
# Create a new ID for our log stream
redef enum Logging::ID += { SSH };
# Define a record with all the columns the log file can have.
# (I'm using a subset of fields from ssh-ext for demonstration.)
type Log: record {
t: time;
id: conn_id; # Will be rolled out into individual columns.
status: string &optional;
country: string &default="unknown";
};
event bro_init()
{
# Create the stream.
Logging::create([$id=SSH, $columns=Log]);
# Add a default filter that simply logs everything to "ssh.log" using the default writer.
#Logging::add_filter(SSH, [$name="default", $path="ssh"]);
}
# Log something.
Logging::log(SSH, [$t=network_time(), $status="ok"]);

View file

@ -1336,7 +1336,9 @@ static int is_init_compat(const BroType* t1, const BroType* t2)
int same_type(const BroType* t1, const BroType* t2, int is_init) int same_type(const BroType* t1, const BroType* t2, int is_init)
{ {
if ( t1 == t2 ) if ( t1 == t2 ||
t1->Tag() == TYPE_ANY ||
t2->Tag() == TYPE_ANY )
return 1; return 1;
t1 = flatten_type(t1); t1 = flatten_type(t1);

View file

@ -3301,6 +3301,10 @@ Val* check_and_promote(Val* v, const BroType* t, int is_init)
TypeTag t_tag = t->Tag(); TypeTag t_tag = t->Tag();
TypeTag v_tag = vt->Tag(); TypeTag v_tag = vt->Tag();
// More thought definitely needs to go into this.
if ( t_tag == TYPE_ANY || v_tag == TYPE_ANY )
return v;
if ( ! EitherArithmetic(t_tag, v_tag) || if ( ! EitherArithmetic(t_tag, v_tag) ||
/* allow sets as initializers */ /* allow sets as initializers */
(is_init && v_tag == TYPE_TABLE) ) (is_init && v_tag == TYPE_TABLE) )

View file

@ -360,6 +360,18 @@ function cat%(...%): string
return new StringVal(s); return new StringVal(s);
%} %}
function logging_log%(id: Logging_ID, rec: any%): bool
%{
// Generate the log line event
// Lookup the log file
TableVal *f = opt_internal_table("Logging::filters");
TableVal *s = f->Lookup(id, 0)->AsTableVal();
// For each filter on 'id'
// Format the output
// Print the line
%}
function cat_sep%(sep: string, def: string, ...%): string function cat_sep%(sep: string, def: string, ...%): string
%{ %{
ODesc d; ODesc d;