diff --git a/scripts/base/frameworks/measurement/main.bro b/scripts/base/frameworks/measurement/main.bro index a8e2950f5a..f649dbe1f2 100644 --- a/scripts/base/frameworks/measurement/main.bro +++ b/scripts/base/frameworks/measurement/main.bro @@ -255,6 +255,11 @@ function reset(m: Measurement) function create(m: Measurement) { + if ( (m?$threshold || m?$threshold_series) && ! m?$threshold_val ) + { + Reporter::error("Measurement given a threshold with no $threshold_val function"); + } + if ( ! m?$id ) m$id=unique_id(""); local tmp: table[Key] of Thresholding = table(); @@ -365,9 +370,6 @@ function threshold_crossed(m: Measurement, key: Key, result: Result) if ( ! m?$threshold_crossed ) return; - #if ( val?$sample_queue ) - # val$samples = Queue::get_str_vector(val$sample_queue); - # Add in the extra ResultVals to make threshold_crossed callbacks easier to write. if ( |m$reducers| != |result| ) { diff --git a/scripts/base/frameworks/measurement/plugins/sample.bro b/scripts/base/frameworks/measurement/plugins/sample.bro index e0084e88d1..018b7c9652 100644 --- a/scripts/base/frameworks/measurement/plugins/sample.bro +++ b/scripts/base/frameworks/measurement/plugins/sample.bro @@ -1,3 +1,4 @@ +@load base/utils/queue module Measurement; @@ -29,7 +30,10 @@ hook add_to_reducer_hook(r: Reducer, val: double, data: DataPoint, rv: ResultVal { if ( ! rv?$sample_queue ) rv$sample_queue = Queue::init([$max_len=r$samples]); - Queue::push(rv$sample_queue, data$str); + if ( ! rv?$samples ) + rv$samples = vector(); + Queue::put(rv$sample_queue, data); + Queue::get_vector(rv$sample_queue, rv$samples); } } diff --git a/scripts/base/frameworks/measurement/plugins/sum.bro b/scripts/base/frameworks/measurement/plugins/sum.bro index 8f989317d8..5a25573870 100644 --- a/scripts/base/frameworks/measurement/plugins/sum.bro +++ b/scripts/base/frameworks/measurement/plugins/sum.bro @@ -27,6 +27,12 @@ function sum_threshold(data_id: string): threshold_function }; } +hook init_resultval_hook(r: Reducer, rv: ResultVal) + { + if ( SUM in r$apply && ! rv?$sum ) + rv$sum = 0; + } + hook add_to_reducer_hook(r: Reducer, val: double, data: DataPoint, rv: ResultVal) { if ( SUM in r$apply ) diff --git a/scripts/base/frameworks/measurement/plugins/unique.bro b/scripts/base/frameworks/measurement/plugins/unique.bro index 5160f0df91..7664663d29 100644 --- a/scripts/base/frameworks/measurement/plugins/unique.bro +++ b/scripts/base/frameworks/measurement/plugins/unique.bro @@ -10,7 +10,7 @@ export { redef record ResultVal += { ## If cardinality is being tracked, the number of unique ## items is tracked here. - unique: count &optional; + unique: count &default=0; }; } diff --git a/scripts/base/utils/queue.bro b/scripts/base/utils/queue.bro index 438529f579..1e7a293e17 100644 --- a/scripts/base/utils/queue.bro +++ b/scripts/base/utils/queue.bro @@ -1,4 +1,4 @@ -##! A FIFO string queue. +##! A FIFO queue. module Queue; @@ -23,17 +23,17 @@ export { ## Push a string onto the top of a queue. ## - ## q: The queue to push the string into. + ## q: The queue to put the value into. ## - ## val: The string to push - global push: function(q: Queue, val: any); + ## val: The value to insert into the queue. + global put: function(q: Queue, val: any); ## Pop a string from the bottom of a queue. ## - ## q: The queue to pop the string from. + ## q: The queue to get the string from. ## - ## Returns: The string popped from the queue. - global pop: function(q: Queue): any; + ## Returns: The value gotten from the queue. + global get: function(q: Queue): any; ## Merge two queue's together. If any settings are applied ## to the queues, the settings from q1 are used for the new @@ -53,23 +53,14 @@ export { ## Returns: The length of the queue. global len: function(q: Queue): count; - ## Get the contents of the queue as a string vector. + ## Get the contents of the queue as a vector. ## ## q: The queue. ## - ## Returns: A :bro:type:`vector of string` containing the - ## current contents of q. - global get_str_vector: function(q: Queue): vector of string; + ## ret: A vector containing the + ## current contents of q as the type of ret. + global get_vector: function(q: Queue, ret: vector of any); - ## Get the contents of the queue as a count vector. Use care - ## with this function. If the data put into the queue wasn't - ## integers you will get conversion errors. - ## - ## q: The queue. - ## - ## Returns: A :bro:type:`vector of count` containing the - ## current contents of q. - global get_cnt_vector: function(q: Queue): vector of count; } redef record Queue += { @@ -96,15 +87,15 @@ function init(s: Settings): Queue return q; } -function push(q: Queue, val: any) +function put(q: Queue, val: any) { if ( q$settings?$max_len && len(q) >= q$settings$max_len ) - pop(q); + get(q); q$vals[q$top] = val; ++q$top; } -function pop(q: Queue): any +function get(q: Queue): any { local ret = q$vals[q$bottom]; delete q$vals[q$bottom]; @@ -120,9 +111,9 @@ function merge(q1: Queue, q2: Queue): Queue for ( ignored_val in q1$vals ) { if ( i in q1$vals ) - push(ret, q1$vals[i]); + put(ret, q1$vals[i]); if ( j in q2$vals ) - push(ret, q2$vals[j]); + put(ret, q2$vals[j]); ++i; ++j; } @@ -134,9 +125,8 @@ function len(q: Queue): count return |q$vals|; } -function get_str_vector(q: Queue): vector of string +function get_vector(q: Queue, ret: vector of any) { - local ret: vector of string; local i = q$bottom; local j = 0; # Really dumb hack, this is only to provide @@ -147,32 +137,7 @@ function get_str_vector(q: Queue): vector of string if ( i >= q$top ) break; - ret[j] = cat(q$vals[i]); + ret[j] = q$vals[i]; ++j; ++i; } - return ret; } - -function get_cnt_vector(q: Queue): vector of count - { - local ret: vector of count; - local i = q$bottom; - local j = 0; - # Really dumb hack, this is only to provide - # the iteration for the correct number of - # values in q$vals. - for ( ignored_val in q$vals ) - { - if ( i >= q$top ) - break; - - # TODO: this is terrible and should be replaced by - # a more generic version of the various - # functions to get vectors of values. - # (the way "any" works right now makes this impossible though) - ret[j] = to_count(cat(q$vals[i])); - ++j; ++i; - } - return ret; - } - diff --git a/scripts/policy/misc/app-metrics.bro b/scripts/policy/misc/app-metrics.bro index d76511fe98..967d5eb88f 100644 --- a/scripts/policy/misc/app-metrics.bro +++ b/scripts/policy/misc/app-metrics.bro @@ -8,22 +8,28 @@ export { redef enum Log::ID += { LOG }; type Info: record { + ## Timestamp when the log line was finished and written. ts: time &log; + ## Time interval that the log line covers. + ts_delta: interval &log; + ## The name of the "app", like "facebook" or "netflix". app: string &log; + ## The number of unique local hosts using the app. uniq_hosts: count &log; + ## The number of hits to the app in total. hits: count &log; + ## The total number of bytes received by users of the app. bytes: count &log; }; ## The frequency of logging the stats collected by this script. - const break_interval = 1min &redef; + const break_interval = 15mins &redef; } redef record connection += { resp_hostname: string &optional; }; - event bro_init() &priority=3 { Log::create_stream(AppMeasurement::LOG, [$columns=Info]); @@ -32,10 +38,11 @@ event bro_init() &priority=3 local r2: Measurement::Reducer = [$stream="apps.hits", $apply=set(Measurement::UNIQUE)]; Measurement::create([$epoch=break_interval, $reducers=set(r1, r2), - $period_finished(data: Measurement::ResultTable) = + $epoch_finished(data: Measurement::ResultTable) = { local l: Info; l$ts = network_time(); + l$ts_delta = break_interval; for ( key in data ) { local result = data[key]; @@ -48,7 +55,7 @@ event bro_init() &priority=3 }]); } -function do_metric(id: conn_id, hostname: string, size: count) +function do_measurement(id: conn_id, hostname: string, size: count) { if ( /\.youtube\.com$/ in hostname && size > 512*1024 ) { @@ -92,11 +99,11 @@ event ssl_established(c: connection) event connection_finished(c: connection) { if ( c?$resp_hostname ) - do_metric(c$id, c$resp_hostname, c$resp$size); + do_measurement(c$id, c$resp_hostname, c$resp$size); } event HTTP::log_http(rec: HTTP::Info) { if( rec?$host ) - do_metric(rec$id, rec$host, rec$response_body_len); + do_measurement(rec$id, rec$host, rec$response_body_len); } diff --git a/scripts/policy/misc/detect-traceroute/main.bro b/scripts/policy/misc/detect-traceroute/main.bro index 7656ed8d03..1b9f369ca5 100644 --- a/scripts/policy/misc/detect-traceroute/main.bro +++ b/scripts/policy/misc/detect-traceroute/main.bro @@ -49,53 +49,45 @@ export { global log_traceroute: event(rec: Traceroute::Info); } -# Track hosts that have sent low TTL packets and which hosts they -# sent them to. -global low_ttlers: set[addr, addr] = {} &create_expire=2min &synchronized; - -function traceroute_detected(src: addr, dst: addr) - { - Log::write(LOG, [$ts=network_time(), $src=src, $dst=dst]); - NOTICE([$note=Traceroute::Detected, - $msg=fmt("%s seems to be running traceroute", src), - $src=src, $dst=dst, - $identifier=cat(src)]); - } - - event bro_init() &priority=5 { Log::create_stream(Traceroute::LOG, [$columns=Info, $ev=log_traceroute]); - Metrics::add_filter("traceroute.time_exceeded", - [$log=F, - $every=icmp_time_exceeded_interval, - $measure=set(Metrics::UNIQUE), + local r1: Measurement::Reducer = [$stream="traceroute.time_exceeded", $apply=set(Measurement::UNIQUE)]; + local r2: Measurement::Reducer = [$stream="traceroute.low_ttl_packet", $apply=set(Measurement::SUM)]; + Measurement::create([$epoch=icmp_time_exceeded_interval, + $reducers=set(r1, r2), + $threshold_val(key: Measurement::Key, result: Measurement::Result) = + { + # Give a threshold value of zero depending on if the host + # sends a low ttl packet. + if ( require_low_ttl_packets && result["traceroute.low_ttl_packet"]$sum == 0 ) + return 0; + else + return result["traceroute.time_exceeded"]$unique; + }, $threshold=icmp_time_exceeded_threshold, - $threshold_crossed(index: Metrics::Index, val: Metrics::ResultVal) = { - local parts = split1(index$str, /-/); + $threshold_crossed(key: Measurement::Key, result: Measurement::Result) = + { + local parts = split1(key$str, /-/); local src = to_addr(parts[1]); local dst = to_addr(parts[2]); - if ( require_low_ttl_packets ) - { - when ( [src, dst] in low_ttlers ) - { - traceroute_detected(src, dst); - } - } - else - traceroute_detected(src, dst); - }]); + Log::write(LOG, [$ts=network_time(), $src=src, $dst=dst]); + NOTICE([$note=Traceroute::Detected, + $msg=fmt("%s seems to be running traceroute", src), + $src=src, $dst=dst, + $identifier=cat(src)]); + }]); } # Low TTL packets are detected with a signature. event signature_match(state: signature_state, msg: string, data: string) { if ( state$sig_id == /traceroute-detector.*/ ) - add low_ttlers[state$conn$id$orig_h, state$conn$id$resp_h]; + Measurement::add_data("traceroute.low_ttl_packet", [$str=cat(state$conn$id$orig_h,"-",state$conn$id$resp_h)], [$num=1]); } event icmp_time_exceeded(c: connection, icmp: icmp_conn, code: count, context: icmp_context) { - Metrics::add_data("traceroute.time_exceeded", [$str=cat(context$id$orig_h,"-",context$id$resp_h)], [$str=cat(c$id$orig_h)]); + Measurement::add_data("traceroute.time_exceeded", [$str=cat(context$id$orig_h,"-",context$id$resp_h)], [$str=cat(c$id$orig_h)]); } diff --git a/scripts/policy/misc/scan.bro b/scripts/policy/misc/scan.bro index 570dbfe6b0..2ea1e9c0fe 100644 --- a/scripts/policy/misc/scan.bro +++ b/scripts/policy/misc/scan.bro @@ -52,59 +52,64 @@ export { } -function check_addr_scan_threshold(index: Metrics::Index, val: Metrics::ResultVal): bool - { - # We don't need to do this if no custom thresholds are defined. - if ( |addr_scan_custom_thresholds| == 0 ) - return F; - - local service = to_port(index$str); - return ( service in addr_scan_custom_thresholds && - val$sum > addr_scan_custom_thresholds[service] ); - } - -function addr_scan_threshold_crossed(index: Metrics::Index, val: Metrics::ResultVal) - { - local side = Site::is_local_addr(index$host) ? "local" : "remote"; - local dur = duration_to_mins_secs(val$end-val$begin); - local message=fmt("%s scanned at least %d unique hosts on port %s in %s", index$host, val$unique, index$str, dur); - - NOTICE([$note=Address_Scan, - $src=index$host, - $p=to_port(index$str), - $sub=side, - $msg=message, - $identifier=cat(index$host)]); - } - -function port_scan_threshold_crossed(index: Metrics::Index, val: Metrics::ResultVal) - { - local side = Site::is_local_addr(index$host) ? "local" : "remote"; - local dur = duration_to_mins_secs(val$end-val$begin); - local message = fmt("%s scanned at least %d unique ports of host %s in %s", index$host, val$unique, index$str, dur); - - NOTICE([$note=Port_Scan, - $src=index$host, - $dst=to_addr(index$str), - $sub=side, - $msg=message, - $identifier=cat(index$host)]); - } +#function check_addr_scan_threshold(key: Measurement::Key, val: Measurement::Result): bool +# { +# # We don't need to do this if no custom thresholds are defined. +# if ( |addr_scan_custom_thresholds| == 0 ) +# return F; +# +# local service = to_port(key$str); +# return ( service in addr_scan_custom_thresholds && +# val$sum > addr_scan_custom_thresholds[service] ); +# } event bro_init() &priority=5 { - # Note: addr scans are trcked similar to: table[src_ip, port] of set(dst); - Metrics::add_filter("scan.addr.fail", [$every=addr_scan_interval, - $measure=set(Metrics::UNIQUE), - $threshold_func=check_addr_scan_threshold, - $threshold=addr_scan_threshold, - $threshold_crossed=addr_scan_threshold_crossed]); + local r1: Measurement::Reducer = [$stream="scan.addr.fail", $apply=set(Measurement::UNIQUE)]; + Measurement::create([$epoch=addr_scan_interval, + $reducers=set(r1), + $threshold_val(key: Measurement::Key, result: Measurement::Result) = + { + return double_to_count(result["scan.addr.fail"]$unique); + }, + #$threshold_func=check_addr_scan_threshold, + $threshold=addr_scan_threshold, + $threshold_crossed(key: Measurement::Key, result: Measurement::Result) = + { + local r = result["scan.addr.fail"]; + local side = Site::is_local_addr(key$host) ? "local" : "remote"; + local dur = duration_to_mins_secs(r$end-r$begin); + local message=fmt("%s scanned at least %d unique hosts on port %s in %s", key$host, r$unique, key$str, dur); + NOTICE([$note=Address_Scan, + $src=key$host, + $p=to_port(key$str), + $sub=side, + $msg=message, + $identifier=cat(key$host)]); + }]); # Note: port scans are tracked similar to: table[src_ip, dst_ip] of set(port); - Metrics::add_filter("scan.port.fail", [$every=port_scan_interval, - $measure=set(Metrics::UNIQUE), - $threshold=port_scan_threshold, - $threshold_crossed=port_scan_threshold_crossed]); + local r2: Measurement::Reducer = [$stream="scan.port.fail", $apply=set(Measurement::UNIQUE)]; + Measurement::create([$epoch=port_scan_interval, + $reducers=set(r2), + $threshold_val(key: Measurement::Key, result: Measurement::Result) = + { + return double_to_count(result["scan.port.fail"]$unique); + }, + $threshold=port_scan_threshold, + $threshold_crossed(key: Measurement::Key, result: Measurement::Result) = + { + local r = result["scan.port.fail"]; + local side = Site::is_local_addr(key$host) ? "local" : "remote"; + local dur = duration_to_mins_secs(r$end-r$begin); + local message = fmt("%s scanned at least %d unique ports of host %s in %s", key$host, r$unique, key$str, dur); + NOTICE([$note=Port_Scan, + $src=key$host, + $dst=to_addr(key$str), + $sub=side, + $msg=message, + $identifier=cat(key$host)]); + }]); } function add_metrics(id: conn_id, reverse: bool) @@ -145,10 +150,10 @@ function add_metrics(id: conn_id, reverse: bool) # return F; if ( hook Scan::addr_scan_policy(scanner, victim, scanned_port) ) - Metrics::add_data("scan.addr.fail", [$host=scanner, $str=cat(scanned_port)], [$str=cat(victim)]); + Measurement::add_data("scan.addr.fail", [$host=scanner, $str=cat(scanned_port)], [$str=cat(victim)]); if ( hook Scan::port_scan_policy(scanner, victim, scanned_port) ) - Metrics::add_data("scan.port.fail", [$host=scanner, $str=cat(victim)], [$str=cat(scanned_port)]); + Measurement::add_data("scan.port.fail", [$host=scanner, $str=cat(victim)], [$str=cat(scanned_port)]); } function is_failed_conn(c: connection): bool diff --git a/scripts/policy/protocols/ftp/detect-bruteforcing.bro b/scripts/policy/protocols/ftp/detect-bruteforcing.bro index 59c8525c7e..286cc95979 100644 --- a/scripts/policy/protocols/ftp/detect-bruteforcing.bro +++ b/scripts/policy/protocols/ftp/detect-bruteforcing.bro @@ -27,9 +27,9 @@ event bro_init() { Metrics::add_filter("ftp.failed_auth", [$every=bruteforce_measurement_interval, $measure=set(Metrics::UNIQUE), - $threshold_val_func(val: Metrics::ResultVal) = { return val$num; }, + $threshold_val_func(val: Metrics::Result) = { return val$num; }, $threshold=bruteforce_threshold, - $threshold_crossed(index: Metrics::Index, val: Metrics::ResultVal) = + $threshold_crossed(index: Metrics::Index, val: Metrics::Result) = { local dur = duration_to_mins_secs(val$end-val$begin); local plural = val$unique>1 ? "s" : ""; diff --git a/scripts/policy/protocols/http/detect-sqli.bro b/scripts/policy/protocols/http/detect-sqli.bro index 410d3fde31..bb47ec2f47 100644 --- a/scripts/policy/protocols/http/detect-sqli.bro +++ b/scripts/policy/protocols/http/detect-sqli.bro @@ -50,11 +50,11 @@ export { | /\/\*![[:digit:]]{5}.*?\*\// &redef; } -function format_sqli_samples(samples: vector of string): string +function format_sqli_samples(samples: vector of Measurement::DataPoint): string { local ret = "SQL Injection samples\n---------------------"; for ( i in samples ) - ret += "\n" + samples[i]; + ret += "\n" + samples[i]$str; return ret; } @@ -63,31 +63,41 @@ event bro_init() &priority=3 # Add filters to the metrics so that the metrics framework knows how to # determine when it looks like an actual attack and how to respond when # thresholds are crossed. - Metrics::add_filter("http.sqli.attacker", - [$every=sqli_requests_interval, - $measure=set(Metrics::SUM), + local r1: Measurement::Reducer = [$stream="http.sqli.attacker", $apply=set(Measurement::SUM), $samples=collect_SQLi_samples]; + Measurement::create([$epoch=sqli_requests_interval, + $reducers=set(r1), + $threshold_val(key: Measurement::Key, result: Measurement::Result) = + { + return double_to_count(result["http.sqli.attacker"]$sum); + }, $threshold=sqli_requests_threshold, - $samples=collect_SQLi_samples, - $threshold_crossed(index: Metrics::Index, val: Metrics::ResultVal) = { + $threshold_crossed(key: Measurement::Key, result: Measurement::Result) = + { + local r = result["http.sqli.attacker"]; NOTICE([$note=SQL_Injection_Attacker, $msg="An SQL injection attacker was discovered!", - $email_body_sections=vector(format_sqli_samples(val$samples)), - $src=index$host, - $identifier=cat(index$host)]); - }]); + $email_body_sections=vector(format_sqli_samples(r$samples)), + $src=key$host, + $identifier=cat(key$host)]); + }]); - Metrics::add_filter("http.sqli.victim", - [$every=sqli_requests_interval, - $measure=set(Metrics::SUM), + local r2: Measurement::Reducer = [$stream="http.sqli.victim", $apply=set(Measurement::SUM), $samples=collect_SQLi_samples]; + Measurement::create([$epoch=sqli_requests_interval, + $reducers=set(r2), + $threshold_val(key: Measurement::Key, result: Measurement::Result) = + { + return double_to_count(result["http.sqli.victim"]$sum); + }, $threshold=sqli_requests_threshold, - $samples=collect_SQLi_samples, - $threshold_crossed(index: Metrics::Index, val: Metrics::ResultVal) = { + $threshold_crossed(key: Measurement::Key, result: Measurement::Result) = + { + local r = result["http.sqli.victim"]; NOTICE([$note=SQL_Injection_Victim, $msg="An SQL injection victim was discovered!", - $email_body_sections=vector(format_sqli_samples(val$samples)), - $src=index$host, - $identifier=cat(index$host)]); - }]); + $email_body_sections=vector(format_sqli_samples(r$samples)), + $src=key$host, + $identifier=cat(key$host)]); + }]); } event http_request(c: connection, method: string, original_URI: string, @@ -97,7 +107,7 @@ event http_request(c: connection, method: string, original_URI: string, { add c$http$tags[URI_SQLI]; - Metrics::add_data("http.sqli.attacker", [$host=c$id$orig_h], [$str=original_URI]); - Metrics::add_data("http.sqli.victim", [$host=c$id$resp_h], [$str=original_URI]); + Measurement::add_data("http.sqli.attacker", [$host=c$id$orig_h], [$str=original_URI]); + Measurement::add_data("http.sqli.victim", [$host=c$id$resp_h], [$str=original_URI]); } } diff --git a/scripts/policy/protocols/ssh/detect-bruteforcing.bro b/scripts/policy/protocols/ssh/detect-bruteforcing.bro index 44e94eb361..cf2d4030fd 100644 --- a/scripts/policy/protocols/ssh/detect-bruteforcing.bro +++ b/scripts/policy/protocols/ssh/detect-bruteforcing.bro @@ -42,21 +42,27 @@ export { event bro_init() { - Metrics::add_filter("ssh.login.failure", [$name="detect-bruteforcing", $log=F, - $every=guessing_timeout, - $measure=set(Metrics::SUM), - $threshold=password_guesses_limit, - $threshold_crossed(index: Metrics::Index, val: Metrics::ResultVal) = { - # Generate the notice. - NOTICE([$note=Password_Guessing, - $msg=fmt("%s appears to be guessing SSH passwords (seen in %.0f connections).", index$host, val$sum), - $src=index$host, - $identifier=cat(index$host)]); - # Insert the guesser into the intel framework. - Intel::insert([$host=index$host, - $meta=[$source="local", - $desc=fmt("Bro observed %0.f apparently failed SSH connections.", val$sum)]]); - }]); + local r1: Measurement::Reducer = [$stream="ssh.login.failure", $apply=set(Measurement::SUM)]; + Measurement::create([$epoch=guessing_timeout, + $reducers=set(r1), + $threshold_val(key: Measurement::Key, result: Measurement::Result) = + { + return double_to_count(result["ssh.login.failure"]$sum); + }, + $threshold=password_guesses_limit, + $threshold_crossed(key: Measurement::Key, result: Measurement::Result) = + { + local r = result["ssh.login.failure"]; + # Generate the notice. + NOTICE([$note=Password_Guessing, + $msg=fmt("%s appears to be guessing SSH passwords (seen in %d connections).", key$host, r$num), + $src=key$host, + $identifier=cat(key$host)]); + # Insert the guesser into the intel framework. + Intel::insert([$host=key$host, + $meta=[$source="local", + $desc=fmt("Bro observed %d apparently failed SSH connections.", r$num)]]); + }]); } event SSH::heuristic_successful_login(c: connection) @@ -76,5 +82,5 @@ event SSH::heuristic_failed_login(c: connection) # be ignored. if ( ! (id$orig_h in ignore_guessers && id$resp_h in ignore_guessers[id$orig_h]) ) - Metrics::add_data("ssh.login.failure", [$host=id$orig_h], [$num=1]); + Measurement::add_data("ssh.login.failure", [$host=id$orig_h], [$num=1]); }