huginn_net_db/
observable_tcp_signals_matching.rs1use crate::db::TcpIndexKey;
2use crate::db_matching_trait::{DatabaseSignature, MatchQuality, ObservedFingerprint};
3use crate::observable_signals::TcpObservation;
4use crate::tcp::{self, IpVersion, PayloadSize};
5
6impl TcpObservation {
7 pub(crate) fn distance_olen(&self, other: &tcp::Signature) -> Option<u32> {
8 if self.olen == other.olen {
9 Some(tcp::TcpMatchQuality::High.as_score())
10 } else {
11 Some(tcp::TcpMatchQuality::Low.as_score())
12 }
13 }
14
15 pub(crate) fn distance_mss(&self, other: &tcp::Signature) -> Option<u32> {
16 if other.mss.is_none() || self.mss == other.mss {
17 Some(tcp::TcpMatchQuality::High.as_score())
18 } else {
19 Some(tcp::TcpMatchQuality::Low.as_score())
20 }
21 }
22
23 pub(crate) fn distance_wscale(&self, other: &tcp::Signature) -> Option<u32> {
24 if other.wscale.is_none() || self.wscale == other.wscale {
25 Some(tcp::TcpMatchQuality::High.as_score())
26 } else {
27 Some(tcp::TcpMatchQuality::Medium.as_score())
28 }
29 }
30
31 pub(crate) fn distance_olayout(&self, other: &tcp::Signature) -> Option<u32> {
32 if self.olayout == other.olayout {
33 Some(tcp::TcpMatchQuality::High.as_score())
34 } else {
35 None
36 }
37 }
38
39 pub(crate) fn distance_quirks(&self, other: &tcp::Signature) -> Option<u32> {
40 if self.quirks == other.quirks {
41 Some(tcp::TcpMatchQuality::High.as_score())
42 } else {
43 None
44 }
45 }
46}
47
48impl ObservedFingerprint for TcpObservation {
49 type Key = TcpIndexKey;
50
51 fn generate_index_key(&self) -> Self::Key {
52 let olayout_parts: Vec<String> = self.olayout.iter().map(|opt| format!("{opt}")).collect();
53 TcpIndexKey {
54 ip_version_key: self.version,
55 olayout_key: olayout_parts.join(","),
56 pclass_key: self.pclass,
57 }
58 }
59}
60
61impl DatabaseSignature<TcpObservation> for tcp::Signature {
62 fn calculate_distance(&self, observed: &TcpObservation) -> Option<u32> {
63 let distance = observed
64 .version
65 .distance_ip_version(&self.version)?
66 .saturating_add(observed.ittl.distance_ttl(&self.ittl)?)
67 .saturating_add(observed.distance_olen(self)?)
68 .saturating_add(observed.distance_mss(self)?)
69 .saturating_add(
70 observed
71 .wsize
72 .distance_window_size(&self.wsize, observed.mss)?,
73 )
74 .saturating_add(observed.distance_wscale(self)?)
75 .saturating_add(observed.distance_olayout(self)?)
76 .saturating_add(observed.distance_quirks(self)?)
77 .saturating_add(observed.pclass.distance_payload_size(&self.pclass)?);
78 Some(distance)
79 }
80
81 fn get_quality_score(&self, distance: u32) -> f32 {
89 tcp::TcpMatchQuality::distance_to_score(distance)
90 }
91
92 fn generate_index_keys_for_db_entry(&self) -> Vec<TcpIndexKey> {
93 let mut keys = Vec::new();
94
95 let olayout_key_str = self
96 .olayout
97 .iter()
98 .map(|opt| format!("{opt}"))
99 .collect::<Vec<String>>()
100 .join(",");
101
102 let versions_for_keys = if self.version == IpVersion::Any {
103 vec![IpVersion::V4, IpVersion::V6]
104 } else {
105 vec![self.version]
106 };
107
108 let pclasses_for_keys = if self.pclass == PayloadSize::Any {
109 vec![PayloadSize::Zero, PayloadSize::NonZero]
110 } else {
111 vec![self.pclass]
112 };
113
114 for v_key_part in &versions_for_keys {
115 for pc_key_part in &pclasses_for_keys {
116 keys.push(TcpIndexKey {
117 ip_version_key: *v_key_part,
118 olayout_key: olayout_key_str.clone(),
119 pclass_key: *pc_key_part,
120 });
121 }
122 }
123
124 keys
125 }
126}