diff --git a/scripts/base/protocols/conn/__load__.bro b/scripts/base/protocols/conn/__load__.bro index 719486d885..7452ffa9c8 100644 --- a/scripts/base/protocols/conn/__load__.bro +++ b/scripts/base/protocols/conn/__load__.bro @@ -2,3 +2,4 @@ @load ./contents @load ./inactivity @load ./polling +@load ./thresholds diff --git a/scripts/base/protocols/conn/thresholds.bro b/scripts/base/protocols/conn/thresholds.bro new file mode 100644 index 0000000000..226ac262ce --- /dev/null +++ b/scripts/base/protocols/conn/thresholds.bro @@ -0,0 +1,274 @@ +##! Implements a generic way to throw events when a connection crosses a +##! fixed threshold of bytes or packets + +module ConnThreshold; + +export { + + type Thresholds: record { + orig_byte_thresholds: set[count] &default=count_set(); ##< current originator byte thresholds we watch for + resp_byte_thresholds: set[count] &default=count_set(); ##< current responder byte thresholds we watch for + orig_packet_thresholds: set[count] &default=count_set(); ##< corrent originator packet thresholds we watch for + resp_packet_thresholds: set[count] &default=count_set(); ##< corrent responder packet thresholds we watch for + }; + + ## Sets a byte threshold for connection sizes, adding it to potentially already existing thresholds. + ## conn_bytes_threshold_crossed will be raised for each set threshold. + ## + ## cid: The connection id. + ## + ## threshold: Threshold in bytes. + ## + ## is_orig: If true, threshold is set for bytes from originator, otherwhise for bytes from responder. + ## + ## Returns: T on success, F on failure. + ## + ## .. bro:see:: bytes_threshold_crossed packets_threshold_crossed set_packets_threshold + ## delete_bytes_threshold delete_packets_threshold + global set_bytes_threshold: function(c: connection, threshold: count, is_orig: bool): bool; + + ## Sets a packet threshold for connection sizes, adding it to potentially already existing thresholds. + ## conn_packets_threshold_crossed will be raised for each set threshold. + ## + ## cid: The connection id. + ## + ## threshold: Threshold in packets. + ## + ## is_orig: If true, threshold is set for packets from originator, otherwhise for packets from responder. + ## + ## Returns: T on success, F on failure. + ## + ## .. bro:see:: bytes_threshold_crossed packets_threshold_crossed set_bytes_threshold + ## delete_bytes_threshold delete_packets_threshold + global set_packets_threshold: function(c: connection, threshold: count, is_orig: bool): bool; + + ## Deletes a byte threshold for connection sizes. + ## + ## cid: The connection id. + ## + ## threshold: Threshold in bytes to remove. + ## + ## is_orig: If true, threshold is removed for packets from originator, otherwhise for packets from responder. + ## + ## Returns: T on success, F on failure. + ## + ## .. bro:see:: bytes_threshold_crossed packets_threshold_crossed set_bytes_threshold set_packets_threshold + ## delete_packets_threshold + global delete_bytes_threshold: function(c: connection, threshold: count, is_orig: bool): bool; + + ## Deletes a packet threshold for connection sizes. + ## + ## cid: The connection id. + ## + ## threshold: Threshold in packets. + ## + ## is_orig: If true, threshold is removed for packets from originator, otherwhise for packets from responder. + ## + ## Returns: T on success, F on failure. + ## + ## .. bro:see:: bytes_threshold_crossed packets_threshold_crossed set_bytes_threshold set_packets_threshold + ## delete_bytes_threshold + global delete_packets_threshold: function(c: connection, threshold: count, is_orig: bool): bool; + + ## Generated for a connection that crossed a set byte threshold + ## + ## c: the connection + ## + ## threshold: the threshold that was set + ## + ## is_orig: True if the threshold was crossed by the originator of the connection + ## + ## .. bro:see:: packets_threshold_crossed set_bytes_threshold set_packets_threshold + ## delete_bytes_threshold delete_packets_threshold + global bytes_threshold_crossed: event(c: connection, threshold: count, is_orig: bool); + + ## Generated for a connection that crossed a set byte threshold + ## + ## c: the connection + ## + ## threshold: the threshold that was set + ## + ## is_orig: True if the threshold was crossed by the originator of the connection + ## + ## .. bro:see:: bytes_threshold_crossed set_bytes_threshold set_packets_threshold + ## delete_bytes_threshold delete_packets_threshold + global packets_threshold_crossed: event(c: connection, threshold: count, is_orig: bool); +} + +redef record connection += { + thresholds: ConnThreshold::Thresholds &optional; +}; + +function set_conn_thresholds(c: connection) + { + if ( c?$thresholds ) + return; + + c$thresholds = Thresholds(); + } + +function find_min_threshold(t: set[count]): count + { + if ( |t| == 0 ) + return 0; + + local first = T; + local min: count = 0; + + for ( i in t ) + { + if ( first ) + { + min = i; + first = F; + } + else + { + if ( i < min ) + min = i; + } + } + + return min; + } + +function set_current_threshold(c: connection, bytes: bool, is_orig: bool): bool + { + local t: count = 0; + local cur: count = 0; + + if ( bytes && is_orig ) + { + t = find_min_threshold(c$thresholds$orig_byte_thresholds); + cur = get_current_conn_bytes_threshold(c$id, is_orig); + } + else if ( bytes && ! is_orig ) + { + t = find_min_threshold(c$thresholds$resp_byte_thresholds); + cur = get_current_conn_bytes_threshold(c$id, is_orig); + } + else if ( ! bytes && is_orig ) + { + t = find_min_threshold(c$thresholds$orig_packet_thresholds); + cur = get_current_conn_packets_threshold(c$id, is_orig); + } + else if ( ! bytes && ! is_orig ) + { + t = find_min_threshold(c$thresholds$resp_packet_thresholds); + cur = get_current_conn_packets_threshold(c$id, is_orig); + } + + if ( t == cur ) + return T; + + if ( bytes && is_orig ) + return set_current_conn_bytes_threshold(c$id, t, T); + else if ( bytes && ! is_orig ) + return set_current_conn_bytes_threshold(c$id, t, F); + else if ( ! bytes && is_orig ) + return set_current_conn_packets_threshold(c$id, t, T); + else if ( ! bytes && ! is_orig ) + return set_current_conn_packets_threshold(c$id, t, F); + } + +function set_bytes_threshold(c: connection, threshold: count, is_orig: bool): bool + { + set_conn_thresholds(c); + + if ( threshold == 0 ) + return F; + + if ( is_orig ) + add c$thresholds$orig_byte_thresholds[threshold]; + else + add c$thresholds$resp_byte_thresholds[threshold]; + + return set_current_threshold(c, T, is_orig); + } + +function set_packets_threshold(c: connection, threshold: count, is_orig: bool): bool + { + set_conn_thresholds(c); + + if ( threshold == 0 ) + return F; + + if ( is_orig ) + add c$thresholds$orig_packet_thresholds[threshold]; + else + add c$thresholds$resp_packet_thresholds[threshold]; + + return set_current_threshold(c, F, is_orig); + } + +function delete_bytes_threshold(c: connection, threshold: count, is_orig: bool): bool + { + set_conn_thresholds(c); + + if ( is_orig && threshold in c$thresholds$orig_byte_thresholds ) + { + delete c$thresholds$orig_byte_thresholds[threshold]; + set_current_threshold(c, T, is_orig); + return T; + } + else if ( ! is_orig && threshold in c$thresholds$resp_byte_thresholds ) + { + delete c$thresholds$resp_byte_thresholds[threshold]; + set_current_threshold(c, T, is_orig); + return T; + } + + return F; + } + +function delete_packets_threshold(c: connection, threshold: count, is_orig: bool): bool + { + set_conn_thresholds(c); + + if ( is_orig && threshold in c$thresholds$orig_packet_thresholds ) + { + delete c$thresholds$orig_packet_thresholds[threshold]; + set_current_threshold(c, F, is_orig); + return T; + } + else if ( ! is_orig && threshold in c$thresholds$resp_packet_thresholds ) + { + delete c$thresholds$resp_packet_thresholds[threshold]; + set_current_threshold(c, F, is_orig); + return T; + } + + return F; + } + +event conn_bytes_threshold_crossed(c: connection, threshold: count, is_orig: bool) &priority=5 + { + if ( is_orig && threshold in c$thresholds$orig_byte_thresholds ) + { + delete c$thresholds$orig_byte_thresholds[threshold]; + event ConnThreshold::bytes_threshold_crossed(c, threshold, is_orig); + } + else if ( ! is_orig && threshold in c$thresholds$resp_byte_thresholds ) + { + delete c$thresholds$resp_byte_thresholds[threshold]; + event ConnThreshold::bytes_threshold_crossed(c, threshold, is_orig); + } + + set_current_threshold(c, T, is_orig); + } + +event conn_packets_threshold_crossed(c: connection, threshold: count, is_orig: bool) &priority=5 + { + if ( is_orig && threshold in c$thresholds$orig_packet_thresholds ) + { + delete c$thresholds$orig_packet_thresholds[threshold]; + event ConnThreshold::packets_threshold_crossed(c, threshold, is_orig); + } + else if ( ! is_orig && threshold in c$thresholds$resp_packet_thresholds ) + { + delete c$thresholds$resp_packet_thresholds[threshold]; + event ConnThreshold::packets_threshold_crossed(c, threshold, is_orig); + } + + set_current_threshold(c, F, is_orig); + } diff --git a/src/analyzer/protocol/conn-size/ConnSize.cc b/src/analyzer/protocol/conn-size/ConnSize.cc index fbe6b87487..160d0bdcc3 100644 --- a/src/analyzer/protocol/conn-size/ConnSize.cc +++ b/src/analyzer/protocol/conn-size/ConnSize.cc @@ -53,15 +53,10 @@ void ConnSize_Analyzer::ThresholdEvent(EventHandlerPtr f, uint64 threshold, bool ConnectionEvent(f, vl); } -void ConnSize_Analyzer::DeliverPacket(int len, const u_char* data, bool is_orig, uint64 seq, const IP_Hdr* ip, int caplen) +void ConnSize_Analyzer::CheckSizes(bool is_orig) { - Analyzer::DeliverPacket(len, data, is_orig, seq, ip, caplen); - if ( is_orig ) { - orig_bytes += ip->TotalLen(); - orig_pkts ++; - if ( orig_bytes_thresh && orig_bytes >= orig_bytes_thresh ) { ThresholdEvent(conn_bytes_threshold_crossed, orig_bytes_thresh, is_orig); @@ -76,9 +71,6 @@ void ConnSize_Analyzer::DeliverPacket(int len, const u_char* data, bool is_orig, } else { - resp_bytes += ip->TotalLen(); - resp_pkts ++; - if ( resp_bytes_thresh && resp_bytes >= resp_bytes_thresh ) { ThresholdEvent(conn_bytes_threshold_crossed, resp_bytes_thresh, is_orig); @@ -93,6 +85,25 @@ void ConnSize_Analyzer::DeliverPacket(int len, const u_char* data, bool is_orig, } } +void ConnSize_Analyzer::DeliverPacket(int len, const u_char* data, bool is_orig, uint64 seq, const IP_Hdr* ip, int caplen) + { + Analyzer::DeliverPacket(len, data, is_orig, seq, ip, caplen); + + if ( is_orig ) + { + orig_bytes += ip->TotalLen(); + orig_pkts ++; + + } + else + { + resp_bytes += ip->TotalLen(); + resp_pkts ++; + } + + CheckSizes(is_orig); + } + void ConnSize_Analyzer::SetThreshold(uint64 threshold, bool bytes, bool orig) { if ( bytes ) @@ -109,6 +120,9 @@ void ConnSize_Analyzer::SetThreshold(uint64 threshold, bool bytes, bool orig) else resp_pkts_thresh = threshold; } + + // check if threshold is already crossed + CheckSizes(orig); } uint64_t ConnSize_Analyzer::GetThreshold(bool bytes, bool orig) diff --git a/src/analyzer/protocol/conn-size/ConnSize.h b/src/analyzer/protocol/conn-size/ConnSize.h index 41388ceb3c..d8dff57a1b 100644 --- a/src/analyzer/protocol/conn-size/ConnSize.h +++ b/src/analyzer/protocol/conn-size/ConnSize.h @@ -30,6 +30,7 @@ public: protected: virtual void DeliverPacket(int len, const u_char* data, bool is_orig, uint64 seq, const IP_Hdr* ip, int caplen); + void CheckSizes(bool is_orig); void ThresholdEvent(EventHandlerPtr f, uint64 threshold, bool is_orig); diff --git a/src/analyzer/protocol/conn-size/events.bif b/src/analyzer/protocol/conn-size/events.bif index ab1034ebb8..14708e55b5 100644 --- a/src/analyzer/protocol/conn-size/events.bif +++ b/src/analyzer/protocol/conn-size/events.bif @@ -1,4 +1,6 @@ -## Generated for a connection that crossed a set byte threshold +## Generated for a connection that crossed a set byte threshold. Note that this +## is a low level event that can fire several times for the same threshold - you +## should probably use ConnThreshold::bytes_threshold_crossed instead. ## ## c: the connection ## @@ -6,11 +8,13 @@ ## ## is_orig: True if the threshold was crossed by the originator of the connection ## -## .. bro:see:: set_conn_packets_threshold set_conn_bytes_threshold conn_packets_threshold_crossed -## get_conn_bytes_threshold get_conn_packets_threshold +## .. bro:see:: set_current_conn_packets_threshold set_current_conn_bytes_threshold conn_packets_threshold_crossed +## get_current_conn_bytes_threshold get_current_conn_packets_threshold event conn_bytes_threshold_crossed%(c: connection, threshold: count, is_orig: bool%); -## Generated for a connection that crossed a set packet threshold +## Generated for a connection that crossed a set packet threshold. Note that this +## is a low level event that can fire several times for the same threshold - you +## should probably use ConnThreshold::packets_threshold_crossed instead. ## ## c: the connection ## @@ -18,6 +22,6 @@ event conn_bytes_threshold_crossed%(c: connection, threshold: count, is_orig: bo ## ## is_orig: True if the threshold was crossed by the originator of the connection ## -## .. bro:see:: set_conn_packets_threshold set_conn_bytes_threshold conn_bytes_threshold_crossed -## get_conn_bytes_threshold get_conn_packets_threshold +## .. bro:see:: set_current_conn_packets_threshold set_current_conn_bytes_threshold conn_bytes_threshold_crossed +## get_current_conn_bytes_threshold get_current_conn_packets_threshold event conn_packets_threshold_crossed%(c: connection, threshold: count, is_orig: bool%); diff --git a/src/analyzer/protocol/conn-size/functions.bif b/src/analyzer/protocol/conn-size/functions.bif index 838c5976c4..cec59a09da 100644 --- a/src/analyzer/protocol/conn-size/functions.bif +++ b/src/analyzer/protocol/conn-size/functions.bif @@ -1,4 +1,3 @@ - %%{ #include "analyzer/protocol/conn-size/ConnSize.h" @@ -20,7 +19,9 @@ analyzer::Analyzer* GetConnsizeAnalyzer(Val* cid) %%} -## Sets a threshold for connection sizes. +## Sets the current byte threshold for connection sizes, overwriting any potential old +## threshold. Be aware that in nearly any case you will want to use the high level API +## instead (ConnThreshold::set_bytes_threshold). ## ## cid: The connection id. ## @@ -28,9 +29,9 @@ analyzer::Analyzer* GetConnsizeAnalyzer(Val* cid) ## ## is_orig: If true, threshold is set for bytes from originator, otherwhise for bytes from responder. ## -## .. bro:see:: set_conn_packets_threshold conn_bytes_threshold_crossed conn_packets_threshold_crossed -## get_conn_bytes_threshold get_conn_packets_threshold -function set_conn_bytes_threshold%(cid: conn_id, threshold: count, is_orig: bool%): bool +## .. bro:see:: set_current_conn_packets_threshold conn_bytes_threshold_crossed conn_packets_threshold_crossed +## get_current_conn_bytes_threshold get_current_conn_packets_threshold +function set_current_conn_bytes_threshold%(cid: conn_id, threshold: count, is_orig: bool%): bool %{ analyzer::Analyzer* a = GetConnsizeAnalyzer(cid); if ( ! a ) @@ -38,10 +39,12 @@ function set_conn_bytes_threshold%(cid: conn_id, threshold: count, is_orig: bool static_cast(a)->SetThreshold(threshold, 1, is_orig); - return new Val(0, TYPE_BOOL); + return new Val(1, TYPE_BOOL); %} -## Sets a threshold for connection packets. +## Sets a threshold for connection packets, overwtiting any potential old thresholds. +## Be aware that in nearly any case you will want to use the high level API +## instead (ConnThreshold::set_packets_threshold). ## ## cid: The connection id. ## @@ -49,9 +52,9 @@ function set_conn_bytes_threshold%(cid: conn_id, threshold: count, is_orig: bool ## ## is_orig: If true, threshold is set for packets from originator, otherwhise for packets from responder. ## -## .. bro:see:: set_conn_bytes_threshold conn_bytes_threshold_crossed conn_packets_threshold_crossed -## get_conn_bytes_threshold get_conn_packets_threshold -function set_conn_packets_threshold%(cid: conn_id, threshold: count, is_orig: bool%): bool +## .. bro:see:: set_current_conn_bytes_threshold conn_bytes_threshold_crossed conn_packets_threshold_crossed +## get_current_conn_bytes_threshold get_current_conn_packets_threshold +function set_current_conn_packets_threshold%(cid: conn_id, threshold: count, is_orig: bool%): bool %{ analyzer::Analyzer* a = GetConnsizeAnalyzer(cid); if ( ! a ) @@ -59,7 +62,7 @@ function set_conn_packets_threshold%(cid: conn_id, threshold: count, is_orig: bo static_cast(a)->SetThreshold(threshold, 0, is_orig); - return new Val(0, TYPE_BOOL); + return new Val(1, TYPE_BOOL); %} ## Gets the current byte threshold size for a connection. @@ -70,9 +73,9 @@ function set_conn_packets_threshold%(cid: conn_id, threshold: count, is_orig: bo ## ## Returns: 0 if no threshold is set or the threshold in bytes ## -## .. bro:see:: set_conn_packets_threshold conn_bytes_threshold_crossed conn_packets_threshold_crossed -## get_conn_packets_threshold -function get_conn_bytes_threshold%(cid: conn_id, is_orig: bool%): count +## .. bro:see:: set_current_conn_packets_threshold conn_bytes_threshold_crossed conn_packets_threshold_crossed +## get_current_conn_packets_threshold +function get_current_conn_bytes_threshold%(cid: conn_id, is_orig: bool%): count %{ analyzer::Analyzer* a = GetConnsizeAnalyzer(cid); if ( ! a ) @@ -91,9 +94,9 @@ function get_conn_bytes_threshold%(cid: conn_id, is_orig: bool%): count ## ## Returns: 0 if no threshold is set or the threshold in packets ## -## .. bro:see:: set_conn_packets_threshold conn_bytes_threshold_crossed conn_packets_threshold_crossed -## get_conn_bytes_threshold -function get_conn_packets_threshold%(cid: conn_id, is_orig: bool%): count +## .. bro:see:: set_current_conn_packets_threshold conn_bytes_threshold_crossed conn_packets_threshold_crossed +## get_current_conn_bytes_threshold +function get_current_conn_packets_threshold%(cid: conn_id, is_orig: bool%): count %{ analyzer::Analyzer* a = GetConnsizeAnalyzer(cid); if ( ! a ) diff --git a/testing/btest/Baseline/scripts.base.protocols.conn.threshold/.stdout b/testing/btest/Baseline/scripts.base.protocols.conn.threshold/.stdout new file mode 100644 index 0000000000..f5ae35abc3 --- /dev/null +++ b/testing/btest/Baseline/scripts.base.protocols.conn.threshold/.stdout @@ -0,0 +1,10 @@ +Threshold set for [orig_h=192.168.1.77, orig_p=57640/tcp, resp_h=66.198.80.67, resp_p=6667/tcp] +triggered bytes, [orig_h=192.168.1.77, orig_p=57640/tcp, resp_h=66.198.80.67, resp_p=6667/tcp], 1, T +triggered bytes, [orig_h=192.168.1.77, orig_p=57640/tcp, resp_h=66.198.80.67, resp_p=6667/tcp], 2000, F +triggered bytes, [orig_h=192.168.1.77, orig_p=57640/tcp, resp_h=66.198.80.67, resp_p=6667/tcp], 2500, T +triggered bytes, [orig_h=192.168.1.77, orig_p=57640/tcp, resp_h=66.198.80.67, resp_p=6667/tcp], 2700, T +triggered packets, [orig_h=192.168.1.77, orig_p=57640/tcp, resp_h=66.198.80.67, resp_p=6667/tcp], 50, F +Threshold set for [orig_h=192.168.1.77, orig_p=57655/tcp, resp_h=209.197.168.151, resp_p=1024/tcp] +triggered bytes, [orig_h=192.168.1.77, orig_p=57655/tcp, resp_h=209.197.168.151, resp_p=1024/tcp], 1, T +triggered bytes, [orig_h=192.168.1.77, orig_p=57655/tcp, resp_h=209.197.168.151, resp_p=1024/tcp], 2000, F +triggered packets, [orig_h=192.168.1.77, orig_p=57640/tcp, resp_h=66.198.80.67, resp_p=6667/tcp], 52, F diff --git a/testing/btest/core/conn-size-threshold.bro b/testing/btest/core/conn-size-threshold.bro index 304683e7e3..ce83e5939d 100644 --- a/testing/btest/core/conn-size-threshold.bro +++ b/testing/btest/core/conn-size-threshold.bro @@ -3,22 +3,22 @@ event connection_established(c: connection) { - print get_conn_bytes_threshold(c$id, T); - print get_conn_bytes_threshold(c$id, F); - print get_conn_packets_threshold(c$id, T); - print get_conn_packets_threshold(c$id, F); + print get_current_conn_bytes_threshold(c$id, T); + print get_current_conn_bytes_threshold(c$id, F); + print get_current_conn_packets_threshold(c$id, T); + print get_current_conn_packets_threshold(c$id, F); print fmt("Threshold set for %s", cat(c$id)); - set_conn_bytes_threshold(c$id, 3000, T); - set_conn_bytes_threshold(c$id, 2000, F); + set_current_conn_bytes_threshold(c$id, 3000, T); + set_current_conn_bytes_threshold(c$id, 2000, F); - set_conn_packets_threshold(c$id, 50, F); - set_conn_packets_threshold(c$id, 63, T); + set_current_conn_packets_threshold(c$id, 50, F); + set_current_conn_packets_threshold(c$id, 63, T); - print get_conn_bytes_threshold(c$id, T); - print get_conn_bytes_threshold(c$id, F); - print get_conn_packets_threshold(c$id, T); - print get_conn_packets_threshold(c$id, F); + print get_current_conn_bytes_threshold(c$id, T); + print get_current_conn_bytes_threshold(c$id, F); + print get_current_conn_packets_threshold(c$id, T); + print get_current_conn_packets_threshold(c$id, F); } event conn_bytes_threshold_crossed(c: connection, threshold: count, is_orig: bool) diff --git a/testing/btest/scripts/base/protocols/conn/threshold.bro b/testing/btest/scripts/base/protocols/conn/threshold.bro new file mode 100644 index 0000000000..13daa8fff0 --- /dev/null +++ b/testing/btest/scripts/base/protocols/conn/threshold.bro @@ -0,0 +1,30 @@ +# @TEST-EXEC: bro -r $TRACES/irc-dcc-send.trace %INPUT +# @TEST-EXEC: btest-diff .stdout + +event connection_established(c: connection) + { + print fmt("Threshold set for %s", cat(c$id)); + ConnThreshold::set_bytes_threshold(c, 1, T); + ConnThreshold::set_bytes_threshold(c, 2500, T); + ConnThreshold::set_bytes_threshold(c, 2700, T); + ConnThreshold::set_bytes_threshold(c, 3000, T); + ConnThreshold::delete_bytes_threshold(c, 3000, T); + ConnThreshold::set_bytes_threshold(c, 2000, F); + + ConnThreshold::set_packets_threshold(c, 50, F); + ConnThreshold::set_packets_threshold(c, 51, F); + ConnThreshold::set_packets_threshold(c, 52, F); + ConnThreshold::delete_packets_threshold(c, 51, F); + ConnThreshold::set_packets_threshold(c, 63, T); + ConnThreshold::delete_packets_threshold(c, 63, T); + } + +event ConnThreshold::bytes_threshold_crossed(c: connection, threshold: count, is_orig: bool) + { + print "triggered bytes", c$id, threshold, is_orig; + } + +event ConnThreshold::packets_threshold_crossed(c: connection, threshold: count, is_orig: bool) + { + print "triggered packets", c$id, threshold, is_orig; + }