summaryrefslogtreecommitdiff
path: root/floss/hcidoc/src/engine.rs
blob: 7c9157a6b49c3545140ba21e34c51cfc0309c9a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
//! Handles stream processing of commands and events.

use chrono::NaiveDateTime;
use std::collections::BTreeMap;
use std::io::Write;

use crate::parser::Packet;

/// Signals are pre-defined indicators that are seen in a packet stream.
pub struct Signal {
    /// Where in the packet stream we see this signal.
    pub index: usize,

    /// Timestamp where this signal is seen.
    pub ts: NaiveDateTime,

    /// Tag identifying the signal. Signals must be pre-defined so we're going
    /// to enforce a static lifetime here.
    pub tag: &'static str,
}

/// Trait that describes a single rule processor. A rule should be used to represent a certain type
/// of analysis (for example: ACL Connections rule may keep track of all ACL connections and report
/// on failed connections).
pub trait Rule {
    /// Process a single packet.
    fn process(&mut self, packet: &Packet);

    /// Generate a report for this rule based on the input stream so far. Usually, this should
    /// report on the instances of this rule that were discovered or any error conditions that are
    /// relevant to this rule.
    fn report(&self, writer: &mut dyn Write);

    /// Report on any signals seen by this rule on the input stream so far. Signals are
    /// structured indicators that specify a specific type of condition that are pre-defined and
    /// used to bucket interesting behavior. Not all reportable events are signals but all signals
    /// are reportable events.
    fn report_signals(&self) -> &[Signal];
}

/// Grouping of rules. This is used to make it easier to enable/disable certain rules for
/// processing a file.
pub struct RuleGroup {
    rules: Vec<Box<dyn Rule>>,
}

impl RuleGroup {
    pub fn new() -> Self {
        RuleGroup { rules: vec![] }
    }

    pub fn add_rule(&mut self, rule: Box<dyn Rule>) {
        self.rules.push(rule);
    }

    pub fn process(&mut self, packet: &Packet) {
        for rule in &mut self.rules {
            rule.process(packet);
        }
    }

    pub fn report(&self, writer: &mut dyn Write) {
        for rule in &self.rules {
            rule.report(writer);
        }
    }

    pub fn report_signals(&self, writer: &mut dyn Write) {
        for rule in &self.rules {
            for signal in rule.report_signals() {
                let _ = writeln!(writer, "({}, {}, {})", signal.index, signal.ts, signal.tag);
            }
        }
    }
}
/// Main entry point to process input data and run rules on them.
pub struct RuleEngine {
    groups: BTreeMap<String, RuleGroup>,
}

impl RuleEngine {
    pub fn new() -> Self {
        RuleEngine { groups: BTreeMap::new() }
    }

    pub fn add_rule_group(&mut self, name: String, group: RuleGroup) {
        self.groups.insert(name, group);
    }

    /// Consume a packet and run it through the various rules processors.
    pub fn process(&mut self, packet: Packet) {
        for group in self.groups.values_mut() {
            group.process(&packet);
        }
    }

    pub fn report(&self, writer: &mut dyn Write) {
        for group in self.groups.values() {
            group.report(writer);
        }
    }

    pub fn report_signals(&self, writer: &mut dyn Write) {
        for group in self.groups.values() {
            group.report_signals(writer);
        }
    }
}