1 //! Handles stream processing of commands and events. 2 3 use chrono::NaiveDateTime; 4 use std::collections::BTreeMap; 5 use std::io::Write; 6 7 use crate::parser::Packet; 8 9 /// Signals are pre-defined indicators that are seen in a packet stream. 10 pub struct Signal { 11 /// Where in the packet stream we see this signal. 12 pub index: usize, 13 14 /// Timestamp where this signal is seen. 15 pub ts: NaiveDateTime, 16 17 /// Tag identifying the signal. Signals must be pre-defined so we're going 18 /// to enforce a static lifetime here. 19 pub tag: &'static str, 20 } 21 22 /// Trait that describes a single rule processor. A rule should be used to represent a certain type 23 /// of analysis (for example: ACL Connections rule may keep track of all ACL connections and report 24 /// on failed connections). 25 pub trait Rule { 26 /// Process a single packet. process(&mut self, packet: &Packet)27 fn process(&mut self, packet: &Packet); 28 29 /// Generate a report for this rule based on the input stream so far. Usually, this should 30 /// report on the instances of this rule that were discovered or any error conditions that are 31 /// relevant to this rule. report(&self, writer: &mut dyn Write)32 fn report(&self, writer: &mut dyn Write); 33 34 /// Report on any signals seen by this rule on the input stream so far. Signals are 35 /// structured indicators that specify a specific type of condition that are pre-defined and 36 /// used to bucket interesting behavior. Not all reportable events are signals but all signals 37 /// are reportable events. report_signals(&self) -> &[Signal]38 fn report_signals(&self) -> &[Signal]; 39 } 40 41 /// Grouping of rules. This is used to make it easier to enable/disable certain rules for 42 /// processing a file. 43 pub struct RuleGroup { 44 rules: Vec<Box<dyn Rule>>, 45 } 46 47 impl RuleGroup { new() -> Self48 pub fn new() -> Self { 49 RuleGroup { rules: vec![] } 50 } 51 add_rule(&mut self, rule: Box<dyn Rule>)52 pub fn add_rule(&mut self, rule: Box<dyn Rule>) { 53 self.rules.push(rule); 54 } 55 process(&mut self, packet: &Packet)56 pub fn process(&mut self, packet: &Packet) { 57 for rule in &mut self.rules { 58 rule.process(packet); 59 } 60 } 61 report(&self, writer: &mut dyn Write)62 pub fn report(&self, writer: &mut dyn Write) { 63 for rule in &self.rules { 64 rule.report(writer); 65 } 66 } 67 report_signals(&self, writer: &mut dyn Write)68 pub fn report_signals(&self, writer: &mut dyn Write) { 69 for rule in &self.rules { 70 for signal in rule.report_signals() { 71 let _ = writeln!(writer, "({}, {}, {})", signal.index, signal.ts, signal.tag); 72 } 73 } 74 } 75 } 76 /// Main entry point to process input data and run rules on them. 77 pub struct RuleEngine { 78 groups: BTreeMap<String, RuleGroup>, 79 } 80 81 impl RuleEngine { new() -> Self82 pub fn new() -> Self { 83 RuleEngine { groups: BTreeMap::new() } 84 } 85 add_rule_group(&mut self, name: String, group: RuleGroup)86 pub fn add_rule_group(&mut self, name: String, group: RuleGroup) { 87 self.groups.insert(name, group); 88 } 89 90 /// Consume a packet and run it through the various rules processors. process(&mut self, packet: Packet)91 pub fn process(&mut self, packet: Packet) { 92 for group in self.groups.values_mut() { 93 group.process(&packet); 94 } 95 } 96 report(&self, writer: &mut dyn Write)97 pub fn report(&self, writer: &mut dyn Write) { 98 for group in self.groups.values() { 99 group.report(writer); 100 } 101 } 102 report_signals(&self, writer: &mut dyn Write)103 pub fn report_signals(&self, writer: &mut dyn Write) { 104 for group in self.groups.values() { 105 group.report_signals(writer); 106 } 107 } 108 } 109