Skip to content

Commit 34005bc

Browse files
authored
Merge pull request #81 from thearossman/main
Add initial (WIP) developer docs
2 parents b631ea6 + 5380d23 commit 34005bc

21 files changed

+400
-166
lines changed

.github/workflows/rustdoc.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
- name: Set up Python ${{ matrix.python-version }}
2323
uses: actions/setup-python@v2
2424
with:
25-
python-version: 3.7
25+
python-version: 3.11
2626
- name: Install pip
2727
run: |
2828
python -m pip install --upgrade pip

core/src/filter/actions.rs

+43-44
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,22 @@
1+
//! For each connection, the Retina framework applies multiple filtering stages as
2+
//! packets are received in order to determine (1) whether packets from that connection
3+
//! should continue to be processed and (2) what to do with these packets.
4+
//!
5+
//! Each connection is associated with a set of Actions. These actions specify the
6+
//! operations the framework will perform for the connection *now or in the future*:
7+
//! e.g., probe for the application-layer protocol (until it is identified), deliver
8+
//! the connection (when it has terminated), deliver all subsequent packets in the
9+
//! connection, etc. An empty Actions struct will cause the connection to be dropped.
10+
//!
11+
//! Each filter stage returns a set of actions and a set of terminal actions.
12+
//! The terminal actions are the subset of actions that are maintained through
13+
//! the next filter stage.
114
use bitmask_enum::bitmask;
2-
/// For each connectionn, the Retina framework applies multiple filtering stages as
3-
/// packets are received in order to determine (1) whether packets from that connection
4-
/// should continue to be processed and (2) what to do with these packets.
5-
///
6-
/// Each connection is associated with a set of Actions. These actions specify the
7-
/// operations the framework will perform for the connection *now or in the future*:
8-
/// e.g., probe for the application-layer protocol (until it is identified), deliver
9-
/// the connection (when it has terminated), deliver all subsequent packets in the
10-
/// connection, etc. An empty Actions struct will cause the connection to be dropped.
11-
///
12-
/// Each filter stage returns a set of actions and a set of terminal actions.
13-
/// The terminal actions are the subset of actions that are maintained through
14-
/// the next filter stage.
1515
use std::fmt;
1616

1717
#[bitmask]
1818
#[bitmask_config(vec_debug)]
1919
pub enum ActionData {
20-
// Packet actions //
2120
/// Forward new packet to connection tracker
2221
/// Should only be used in the PacketContinue filter
2322
PacketContinue,
@@ -36,7 +35,6 @@ pub enum ActionData {
3635
/// datatype that requires tracking and delivering packets.
3736
PacketTrack,
3837

39-
// Connection/session actions //
4038
/// Probe for (identify) the application-layer protocol
4139
ProtoProbe,
4240
/// Once the application-layer protocl is identified, apply the ProtocolFilter.
@@ -60,6 +58,7 @@ pub enum ActionData {
6058
ConnDeliver,
6159
}
6260

61+
/// Actions maintained per-connection
6362
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
6463
pub struct Actions {
6564
/// All actions (terminal and non-terminal) that should
@@ -69,7 +68,7 @@ pub struct Actions {
6968
/// regardless of what the next filter returns
7069
/// E.g., if a terminal match for a connection-level filter
7170
/// occurs at the packet layer, we should continue tracking
72-
/// the connection without re-applying that filter.
71+
/// the connection regardless of later filter results.
7372
pub terminal_actions: ActionData,
7473
}
7574

@@ -80,46 +79,46 @@ impl Default for Actions {
8079
}
8180

8281
impl Actions {
83-
/// Create an empty Actions bitmask
82+
// Create an empty Actions bitmask
8483
pub fn new() -> Self {
8584
Self {
8685
data: ActionData::none(),
8786
terminal_actions: ActionData::none(),
8887
}
8988
}
9089

91-
/// Store the result of a new filter
92-
/// Used at runtime after application of next filter
90+
// Store the result of a new filter
91+
// Used at runtime after application of next filter
9392
#[inline]
9493
pub fn update(&mut self, actions: &Actions) {
9594
self.data = self.terminal_actions | actions.data;
9695
self.terminal_actions |= actions.terminal_actions;
9796
}
9897

99-
/// Combine terminal and non-terminal actions
100-
/// Used for building a filter tree at compile time and when
101-
/// applying a filter at runtime if additional conditions are met.
98+
// Combine terminal and non-terminal actions
99+
// Used for building a filter tree at compile time and when
100+
// applying a filter at runtime if additional conditions are met.
102101
#[inline]
103102
pub fn push(&mut self, actions: &Actions) {
104103
self.data |= actions.data;
105104
self.terminal_actions |= actions.terminal_actions;
106105
}
107106

108-
/// Returns true if no actions are set (i.e., the connection can
109-
/// be dropped by the framework).
107+
// Returns true if no actions are set (i.e., the connection can
108+
// be dropped by the framework).
110109
#[inline]
111110
pub fn drop(&self) -> bool {
112111
self.data.is_none() && self.terminal_actions.is_none()
113112
}
114113

115-
/// Update `self` to contain only actions not in `actions`
114+
// Update `self` to contain only actions not in `actions`
116115
#[inline]
117116
pub(crate) fn clear_intersection(&mut self, actions: &Actions) {
118117
self.data &= actions.data.not();
119118
self.terminal_actions &= actions.data.not();
120119
}
121120

122-
/// Conn tracker must deliver each PDU to tracked data when received
121+
// Conn tracker must deliver each PDU to tracked data when received
123122
#[inline]
124123
pub(crate) fn update_pdu(&self) -> bool {
125124
self.data.intersects(ActionData::UpdatePDU)
@@ -148,7 +147,7 @@ impl Actions {
148147
self.data.intersects(ActionData::PacketCache)
149148
}
150149

151-
/// True if application-layer probing or parsing should be applied
150+
// True if application-layer probing or parsing should be applied
152151
#[inline]
153152
pub(crate) fn parse_any(&self) -> bool {
154153
self.data.intersects(
@@ -167,51 +166,51 @@ impl Actions {
167166
self.data == ActionData::ConnDeliver
168167
}
169168

170-
/// True if the session filter should be applied
169+
// True if the session filter should be applied
171170
#[inline]
172171
pub(crate) fn apply_session_filter(&mut self) -> bool {
173172
// \note deliver filter is in session filter
174173
self.data
175174
.intersects(ActionData::SessionFilter | ActionData::SessionDeliver)
176175
}
177176

178-
/// True if the protocol filter should be applied
177+
// True if the protocol filter should be applied
179178
#[inline]
180179
pub(crate) fn apply_proto_filter(&mut self) -> bool {
181180
self.data.contains(ActionData::ProtoFilter)
182181
}
183182

184-
/// True if the framework should probe for the app-layer protocol
183+
// True if the framework should probe for the app-layer protocol
185184
#[inline]
186185
pub(crate) fn session_probe(&self) -> bool {
187186
self.data
188187
.intersects(ActionData::ProtoProbe | ActionData::ProtoFilter)
189188
}
190189

191-
/// True if the framework should parse application-layer data
190+
// True if the framework should parse application-layer data
192191
#[inline]
193192
pub(crate) fn session_parse(&self) -> bool {
194193
self.data.intersects(
195194
ActionData::SessionDeliver | ActionData::SessionFilter | ActionData::SessionTrack,
196195
) && !self.session_probe() // still at probing stage
197196
}
198197

199-
/// True if the framework should buffer parsed sessions
198+
// True if the framework should buffer parsed sessions
200199
#[inline]
201200
pub(crate) fn session_track(&self) -> bool {
202201
self.data.intersects(ActionData::SessionTrack)
203202
}
204203

205-
/// True if the framework should deliver future packets in this connection
204+
// True if the framework should deliver future packets in this connection
206205
#[inline]
207206
pub(crate) fn packet_deliver(&self) -> bool {
208207
self.data.intersects(ActionData::PacketDeliver)
209208
}
210209

211-
/// After parsing a session, the framework must decide whether to continue
212-
/// probing for sessions depending on the protocol
213-
/// If no further parsing is required (e.g., TLS Handshake), this method
214-
/// should be invoked.
210+
// After parsing a session, the framework must decide whether to continue
211+
// probing for sessions depending on the protocol
212+
// If no further parsing is required (e.g., TLS Handshake), this method
213+
// should be invoked.
215214
#[inline]
216215
pub(crate) fn session_clear_parse(&mut self) {
217216
self.clear_mask(
@@ -222,8 +221,8 @@ impl Actions {
222221
);
223222
}
224223

225-
/// Subscription requires protocol probe/parse but matched at packet stage
226-
/// Update action to reflect state transition to protocol parsing
224+
// Subscription requires protocol probe/parse but matched at packet stage
225+
// Update action to reflect state transition to protocol parsing
227226
#[inline]
228227
pub(crate) fn session_done_probe(&mut self) {
229228
if self.terminal_actions.contains(ActionData::ProtoProbe) {
@@ -235,8 +234,8 @@ impl Actions {
235234
}
236235
}
237236

238-
/// Some app-layer protocols revert to probing after session is parsed
239-
/// This is done if more sessions are expected
237+
// Some app-layer protocols revert to probing after session is parsed
238+
// This is done if more sessions are expected
240239
pub(crate) fn session_set_probe(&mut self) {
241240
// If protocol probing was set at the PacketFilter stage (i.e.,
242241
// terminal match for a subscription that requires parsing sessions),
@@ -263,20 +262,20 @@ impl Actions {
263262
*/
264263
}
265264

266-
/// True if the connection should be delivered at termination
265+
// True if the connection should be delivered at termination
267266
#[inline]
268267
pub(crate) fn connection_matched(&self) -> bool {
269268
self.terminal_actions.intersects(ActionData::ConnDeliver)
270269
}
271270

272-
/// Clear all actions
271+
// Clear all actions
273272
#[inline]
274273
pub(crate) fn clear(&mut self) {
275274
self.terminal_actions = ActionData::none();
276275
self.data = ActionData::none();
277276
}
278277

279-
/// Clear a subset of actions
278+
// Clear a subset of actions
280279
#[inline]
281280
pub(crate) fn clear_mask(&mut self, mask: ActionData) {
282281
self.data &= mask.not();

core/src/filter/ast.rs

+27-23
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ lazy_static! {
4343
}
4444

4545
lazy_static! {
46+
/// Graph of possible protocol layers used to build the filter tree.
47+
/// For example, "tls" must be preceded by "tcp".
4648
pub(crate) static ref NODE_BIMAP: BiMap::<NodeIndex, ProtocolName> = {
4749
LAYERS
4850
.node_indices()
@@ -51,8 +53,8 @@ lazy_static! {
5153
};
5254
}
5355

54-
/// Returns `true` if there is a path from `from` to `to` in the
55-
/// protocol LAYERS graph.
56+
// Returns `true` if there is a path from `from` to `to` in the
57+
// protocol LAYERS graph.
5658
fn has_path(from: &ProtocolName, to: &ProtocolName) -> bool {
5759
// Returns `false` if from == to
5860
let from_node = NODE_BIMAP.get_by_right(from);
@@ -72,9 +74,9 @@ fn has_path(from: &ProtocolName, to: &ProtocolName) -> bool {
7274
/// An individual filter predicate
7375
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
7476
pub enum Predicate {
75-
Unary {
76-
protocol: ProtocolName,
77-
},
77+
/// Matches on a protocol
78+
Unary { protocol: ProtocolName },
79+
/// Matches on a field in a protocol
7880
Binary {
7981
protocol: ProtocolName,
8082
field: FieldName,
@@ -84,32 +86,32 @@ pub enum Predicate {
8486
}
8587

8688
impl Predicate {
87-
/// Returns the name of the protocol.
89+
// Returns the name of the protocol.
8890
pub fn get_protocol(&self) -> &ProtocolName {
8991
match self {
9092
Predicate::Unary { protocol } => protocol,
9193
Predicate::Binary { protocol, .. } => protocol,
9294
}
9395
}
9496

95-
/// Returns `true` if predicate is a unary constraint.
97+
// Returns `true` if predicate is a unary constraint.
9698
pub fn is_unary(&self) -> bool {
9799
matches!(self, Predicate::Unary { .. })
98100
}
99101

100-
/// Returns `true` if predicate is a binary constraint.
102+
// Returns `true` if predicate is a binary constraint.
101103
pub fn is_binary(&self) -> bool {
102104
matches!(self, Predicate::Binary { .. })
103105
}
104106

105-
/// Returns `true` if predicate can be pushed to a packet filter.
106-
/// i.e., the lowest filter level needed to apply the predicate is a packet filter.
107+
// Returns `true` if predicate can be pushed to a packet filter.
108+
// i.e., the lowest filter level needed to apply the predicate is a packet filter.
107109
pub fn on_packet(&self) -> bool {
108110
!self.needs_conntrack()
109111
}
110112

111-
/// Returns `true` if predicate *requires* raw packets
112-
/// (i.e., cannot be connection-level data)
113+
// Returns `true` if predicate *requires* raw packets
114+
// (i.e., cannot be connection-level data)
113115
pub fn req_packet(&self) -> bool {
114116
if !self.on_packet() {
115117
return false;
@@ -127,20 +129,20 @@ impl Predicate {
127129
!ConnData::supported_protocols().contains(&self.get_protocol().name())
128130
}
129131

130-
/// Returns `true` if predicate can be satisfied by a connection filter.
131-
/// i.e., the lowest filter level needed to apply the predicate is a connection filter.
132+
// Returns `true` if predicate can be satisfied by a connection filter.
133+
// i.e., the lowest filter level needed to apply the predicate is a connection filter.
132134
pub fn on_proto(&self) -> bool {
133135
self.needs_conntrack() && self.is_unary()
134136
}
135137

136-
/// Returns `true` if predicate can be satisfied by a session filter.
137-
/// i.e., the lowest filter level needed to apply the predicate is a session filter.
138+
// Returns `true` if predicate can be satisfied by a session filter.
139+
// i.e., the lowest filter level needed to apply the predicate is a session filter.
138140
pub fn on_session(&self) -> bool {
139141
self.needs_conntrack() && self.is_binary()
140142
}
141143

142-
/// Returns `true` if the predicate's protocol requires connection tracking
143-
/// i.e., is an application-layer protocol that runs on top of TCP or UDP.
144+
// Returns `true` if the predicate's protocol requires connection tracking
145+
// i.e., is an application-layer protocol that runs on top of TCP or UDP.
144146
fn needs_conntrack(&self) -> bool {
145147
has_path(self.get_protocol(), &protocol!("tcp"))
146148
|| has_path(self.get_protocol(), &protocol!("udp"))
@@ -156,6 +158,8 @@ impl Predicate {
156158
}
157159
}
158160

161+
// Returns `true` if the predicate would have been checked at the previous
162+
// filter layer based on both the filter layer and the subscription level.
159163
pub(super) fn is_prev_layer(
160164
&self,
161165
filter_layer: FilterLayer,
@@ -187,7 +191,7 @@ impl Predicate {
187191
}
188192
}
189193

190-
// Predicate would have been checked at prev. layer
194+
// Returns true if the predicate would have been checked at prev. layer
191195
// Does not consider subscription type; meant to be used for filter collapse.
192196
pub(super) fn is_prev_layer_pred(&self, filter_layer: FilterLayer) -> bool {
193197
match filter_layer {
@@ -204,13 +208,13 @@ impl Predicate {
204208
}
205209
}
206210

207-
/// Returns `true` if predicate can be pushed down to hardware port.
211+
// Returns `true` if predicate can be pushed down to hardware port.
208212
pub(super) fn is_hardware_filterable(&self, port: &Port) -> bool {
209213
hardware::device_supported(self, port)
210214
}
211215

212-
/// Returns `true` if `self` and `pred` are entirely mutually exclusive
213-
/// (i.e., could be correctly represented by "if `a` {} else if `b` {}"...)
216+
// Returns `true` if `self` and `pred` are entirely mutually exclusive
217+
// (i.e., could be correctly represented by "if `a` {} else if `b` {}"...)
214218
pub(super) fn is_excl(&self, pred: &Predicate) -> bool {
215219
// Unary predicates at the same layer are mutually exclusive
216220
// E.g.: `ipv4 | ipv6`, `tcp | udp`
@@ -301,7 +305,7 @@ impl Predicate {
301305
false
302306
}
303307

304-
/// Returns `true` if `self` is a subset of `pred` (`pred` is parent of)
308+
// Returns `true` if `self` is a subset of `pred` (`pred` is parent of)
305309
pub(super) fn is_child(&self, pred: &Predicate) -> bool {
306310
if self.get_protocol() != pred.get_protocol() {
307311
return false;

0 commit comments

Comments
 (0)