@push.rocks/smartnftables 4.4.0 → 4.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,13 +6,26 @@
6
6
  //! rules prove through `private_link`), the protocols, the live and originally
7
7
  //! tracked tuple and the generation's conntrack zone. Generation labels live in
8
8
  //! two exact sets, so a label alone never authorizes a changed grant.
9
- use super::keys::{self, Key, Loads, ADDRESS, INDEX, LABEL, PORT, PROTOCOL, ZONE};
9
+ use super::keys::{
10
+ self, Field, Key, Loads, CT_LABEL, CT_ORIGINAL_DADDR, CT_ORIGINAL_DPORT, CT_ORIGINAL_SADDR,
11
+ CT_ORIGINAL_SPORT, CT_PROTOCOL, CT_ZONE, IIF, IP_DADDR, IP_SADDR, L4PROTO, TH_DPORT, TH_SPORT,
12
+ };
10
13
  use super::private::known;
11
14
  use super::*;
12
15
 
13
16
  const LABEL_SET: &str = "leased_label";
14
17
  const GENERATION_MAP: &str = "leased_generation";
15
- const RETURN: [u32; 5] = [PROTOCOL, ADDRESS, ADDRESS, PORT, PORT];
18
+ const LABEL_KEY: [Field; 2] = [CT_ZONE, CT_LABEL];
19
+ /// The generation map's key is the zone padded to its register, as every
20
+ /// release before typed sets declared it.
21
+ const GENERATION_KEY: [Field; 1] = [CT_ZONE];
22
+ const GENERATION_DATA: [Field; 1] = [CT_LABEL];
23
+ const RETURN: [Field; 5] = [L4PROTO, IP_SADDR, IP_DADDR, TH_SPORT, TH_DPORT];
24
+ /// The zone a classification stores.
25
+ const ZONE_DATA: [Field; 1] = [CT_ZONE];
26
+ /// The source NAT a flow map stores: the transit address and the first and
27
+ /// last port of the leased range.
28
+ const TRANSLATION: [Field; 3] = [IP_SADDR, TH_SPORT, TH_SPORT];
16
29
 
17
30
  /// Platform grants precede the protected barrier and public grants follow it.
18
31
  #[derive(Clone, Copy, PartialEq, Eq)]
@@ -69,24 +82,26 @@ impl Shape {
69
82
  }
70
83
  /// A source port field only where the grants name one, then the
71
84
  /// destination port.
72
- fn ports(self) -> Vec<u32> {
85
+ fn ports(self, source: Field, destination: Field) -> Vec<Field> {
73
86
  if self.sourced {
74
- vec![PORT, PORT]
87
+ vec![source, destination]
75
88
  } else {
76
- vec![PORT]
89
+ vec![destination]
77
90
  }
78
91
  }
79
- fn classification(self) -> Vec<u32> {
80
- let mut fields = vec![INDEX, PROTOCOL, ADDRESS, ADDRESS];
81
- fields.extend(self.ports());
92
+ /// The classification key, as the incoming direction loads it.
93
+ fn classification(self) -> Vec<Field> {
94
+ let mut fields = vec![IIF, L4PROTO, IP_SADDR, IP_DADDR];
95
+ fields.extend(self.ports(TH_SPORT, TH_DPORT));
82
96
  fields
83
97
  }
84
- fn flow(self) -> Vec<u32> {
85
- let mut fields = vec![INDEX, PROTOCOL, PROTOCOL, ADDRESS, ADDRESS];
86
- fields.extend(self.ports());
87
- fields.extend([ADDRESS, ADDRESS]);
88
- fields.extend(self.ports());
89
- fields.push(ZONE);
98
+ /// The flow key, as the original direction loads it.
99
+ fn flow(self) -> Vec<Field> {
100
+ let mut fields = vec![IIF, L4PROTO, CT_PROTOCOL, IP_SADDR, IP_DADDR];
101
+ fields.extend(self.ports(TH_SPORT, TH_DPORT));
102
+ fields.extend([CT_ORIGINAL_SADDR, CT_ORIGINAL_DADDR]);
103
+ fields.extend(self.ports(CT_ORIGINAL_SPORT, CT_ORIGINAL_DPORT));
104
+ fields.push(CT_ZONE);
90
105
  fields
91
106
  }
92
107
  fn key_ports(self, key: Key, grant: &Grant) -> Key {
@@ -151,8 +166,15 @@ pub(super) fn sets(program: &mut Program<'_>, scope: &RouterScope) -> Result<()>
151
166
  labels.push((key, None));
152
167
  generations.push((zone_word(generation.conntrack_zone), Some(label)));
153
168
  }
154
- keys::exact(program, LABEL_SET, 38, 4 + LABEL, None, labels)?;
155
- keys::exact(program, GENERATION_MAP, 39, 4, Some(LABEL), generations)?;
169
+ keys::exact(program, LABEL_SET, 38, &LABEL_KEY, None, labels)?;
170
+ keys::exact(
171
+ program,
172
+ GENERATION_MAP,
173
+ 39,
174
+ &GENERATION_KEY,
175
+ Some(&GENERATION_DATA),
176
+ generations,
177
+ )?;
156
178
  for kind in [Kind::Platform, Kind::Public] {
157
179
  let mut returns = Vec::new();
158
180
  for shape in Shape::all(kind) {
@@ -212,7 +234,7 @@ pub(super) fn sets(program: &mut Program<'_>, scope: &RouterScope) -> Result<()>
212
234
  &shape.name("zone"),
213
235
  shape.id(0),
214
236
  &shape.classification(),
215
- Some(ZONE),
237
+ Some(&ZONE_DATA),
216
238
  classification,
217
239
  )?;
218
240
  keys::interval(
@@ -220,12 +242,19 @@ pub(super) fn sets(program: &mut Program<'_>, scope: &RouterScope) -> Result<()>
220
242
  &shape.name("flow"),
221
243
  shape.id(1),
222
244
  &shape.flow(),
223
- Some(12),
245
+ Some(&TRANSLATION),
224
246
  flows,
225
247
  )?;
226
248
  }
227
249
  let id = 48 + 8 + u32::from(kind == Kind::Public);
228
- keys::interval(program, &kind.returns(), id, &RETURN, Some(ZONE), returns)?;
250
+ keys::interval(
251
+ program,
252
+ &kind.returns(),
253
+ id,
254
+ &RETURN,
255
+ Some(&ZONE_DATA),
256
+ returns,
257
+ )?;
229
258
  }
230
259
  Ok(())
231
260
  }
@@ -260,15 +289,15 @@ pub(super) fn classification(
260
289
  };
261
290
  if !present(scope, shape, false).is_empty() {
262
291
  let mut expressions = ipv4();
263
- expressions.extend(known(true));
264
- expressions.extend(tuple(true).map(&shape.name("zone")));
292
+ expressions.extend(known(true)?);
293
+ expressions.extend(tuple(true).map(&shape.name("zone"), &shape.classification())?);
265
294
  expressions.push(zone_from_data());
266
295
  program.end("raw_in", expressions, 1)?;
267
296
  }
268
297
  if !present(scope, shape, true).is_empty() {
269
298
  let mut expressions = ipv4();
270
299
  expressions.extend(link(&scope.handoff, false));
271
- expressions.extend(tuple(false).map(&shape.name("zone")));
300
+ expressions.extend(tuple(false).map(&shape.name("zone"), &shape.classification())?);
272
301
  expressions.push(zone_from_data());
273
302
  program.end("raw_out", expressions, 1)?;
274
303
  }
@@ -283,7 +312,7 @@ pub(super) fn classification(
283
312
  .address(false)
284
313
  .port(true)
285
314
  .port(false);
286
- expressions.extend(loads.map(&kind.returns()));
315
+ expressions.extend(loads.map(&kind.returns(), &RETURN)?);
287
316
  expressions.push(zone_from_data());
288
317
  program.end("raw_return", expressions, 1)?;
289
318
  }
@@ -305,8 +334,8 @@ fn flow(shape: Shape, reply: bool) -> Loads {
305
334
  loads.zone();
306
335
  loads
307
336
  }
308
- fn labelled() -> Vec<Attr> {
309
- Loads::new().zone().label().lookup(LABEL_SET)
337
+ fn labelled() -> Result<Vec<Attr>> {
338
+ Loads::new().zone().label().lookup(LABEL_SET, &LABEL_KEY)
310
339
  }
311
340
  /// Admissions of one kind in one filter chain. The original direction leaves
312
341
  /// through the handoff: ESTABLISHED or NEW with the generation's label, or a
@@ -340,36 +369,36 @@ pub(super) fn admissions(
340
369
  };
341
370
  if !original.is_empty() {
342
371
  let mut established = envelope(false, 2);
343
- established.extend(known(true));
344
- established.extend(flow(shape, false).lookup(&name));
345
- established.extend(labelled());
372
+ established.extend(known(true)?);
373
+ established.extend(flow(shape, false).lookup(&name, &shape.flow())?);
374
+ established.extend(labelled()?);
346
375
  program.end(chain, established, 1)?;
347
376
  for transport in &original {
348
- let opening = || {
377
+ let opening = || -> Result<Vec<Attr>> {
349
378
  let mut result = envelope(false, 8);
350
379
  result.extend(meta(16, vec![protocol(transport)]));
351
380
  if *transport == "tcp" {
352
381
  result.extend(opening_tcp());
353
382
  }
354
- result.extend(known(true));
355
- result.extend(flow(shape, false).lookup(&name));
356
- result
383
+ result.extend(known(true)?);
384
+ result.extend(flow(shape, false).lookup(&name, &shape.flow())?);
385
+ Ok(result)
357
386
  };
358
- let mut labelled_opening = opening();
359
- labelled_opening.extend(labelled());
387
+ let mut labelled_opening = opening()?;
388
+ labelled_opening.extend(labelled()?);
360
389
  program.end(chain, labelled_opening, 1)?;
361
- let mut fresh = opening();
390
+ let mut fresh = opening()?;
362
391
  fresh.extend(ct(13, None, vec![0; 16]));
363
- fresh.extend(Loads::new().zone().map(GENERATION_MAP));
392
+ fresh.extend(Loads::new().zone().map(GENERATION_MAP, &GENERATION_KEY)?);
364
393
  fresh.push(keys::set_ct_from_data(13));
365
394
  program.end(chain, fresh, 1)?;
366
395
  }
367
396
  }
368
397
  if reply {
369
398
  let mut answer = envelope(true, 2);
370
- answer.extend(known(false));
371
- answer.extend(flow(shape, true).lookup(&name));
372
- answer.extend(labelled());
399
+ answer.extend(known(false)?);
400
+ answer.extend(flow(shape, true).lookup(&name, &shape.flow())?);
401
+ answer.extend(labelled()?);
373
402
  program.end(chain, answer, 1)?;
374
403
  }
375
404
  }
@@ -389,9 +418,9 @@ pub(super) fn translation(
389
418
  let mut expressions = ipv4();
390
419
  expressions.extend(link(&scope.handoff, false));
391
420
  expressions.extend(ct(1, None, vec![0]));
392
- expressions.extend(labelled());
393
- expressions.extend(known(true));
394
- expressions.extend(flow(shape, false).map(&shape.name("flow")));
421
+ expressions.extend(labelled()?);
422
+ expressions.extend(known(true)?);
423
+ expressions.extend(flow(shape, false).map(&shape.name("flow"), &shape.flow())?);
395
424
  expressions.push(super::published::nat(0, 2));
396
425
  program.rule("post", expressions)?;
397
426
  }
@@ -1,10 +1,13 @@
1
- use super::keys::{self, Key, Loads, ADDRESS, PORT, PROTOCOL};
1
+ use super::keys::{
2
+ self, Field, Key, Loads, CT_ORIGINAL_DPORT, CT_REPLY_SADDR, CT_REPLY_SPORT, L4PROTO,
3
+ };
2
4
  use super::*;
3
5
 
4
6
  /// Publications translated into a pool: the protocol, the address and port the
5
7
  /// flow was translated to, and its originally tracked destination port. Port fields are inclusive intervals, so one element holds
6
8
  /// one port or a whole range.
7
9
  const PUBLISHED: &str = "pool_published";
10
+ const PUBLISHED_KEY: [Field; 4] = [L4PROTO, CT_REPLY_SADDR, CT_REPLY_SPORT, CT_ORIGINAL_DPORT];
8
11
 
9
12
  pub(super) fn compile(program: &mut Program<'_>, scope: &AllocationPoolGuardScope) -> Result<()> {
10
13
  for (name, hook) in [("input", 1), ("forward", 2), ("output", 3)] {
@@ -67,15 +70,7 @@ fn published_set(program: &mut Program<'_>, scope: &AllocationPoolGuardScope) ->
67
70
  .element(None),
68
71
  );
69
72
  }
70
- keys::interval(
71
- program,
72
- PUBLISHED,
73
- 2,
74
- &[PROTOCOL, ADDRESS, PORT, PORT],
75
- None,
76
- elements,
77
- )
78
- .map(|_| ())
73
+ keys::interval(program, PUBLISHED, 2, &PUBLISHED_KEY, None, elements).map(|_| ())
79
74
  }
80
75
  /// The admissions of every publication, whatever their number: behind the
81
76
  /// default conntrack zone, an ESTABLISHED packet of either direction, and a NEW
@@ -90,7 +85,7 @@ fn published(program: &mut Program<'_>, scope: &AllocationPoolGuardScope) -> Res
90
85
  let lookup = || {
91
86
  let mut loads = Loads::new();
92
87
  loads.protocol().translated().original_port(false);
93
- loads.lookup(PUBLISHED)
88
+ loads.lookup(PUBLISHED, &PUBLISHED_KEY)
94
89
  };
95
90
  let envelope = |connection_state: u32| {
96
91
  let mut result = ct(17, None, 0_u16.to_ne_bytes().to_vec());
@@ -98,7 +93,7 @@ fn published(program: &mut Program<'_>, scope: &AllocationPoolGuardScope) -> Res
98
93
  result
99
94
  };
100
95
  let mut established = envelope(2);
101
- established.extend(lookup());
96
+ established.extend(lookup()?);
102
97
  program.end("pool_guard", established, 1)?;
103
98
  let transports: std::collections::BTreeSet<&str> = scope
104
99
  .published_ports
@@ -111,7 +106,7 @@ fn published(program: &mut Program<'_>, scope: &AllocationPoolGuardScope) -> Res
111
106
  if transport == "tcp" {
112
107
  opening.extend(opening_tcp());
113
108
  }
114
- opening.extend(lookup());
109
+ opening.extend(lookup()?);
115
110
  program.end("pool_guard", opening, 1)?;
116
111
  }
117
112
  Ok(())
@@ -4,7 +4,9 @@
4
4
  //! anti-spoof guard, the directed rules and the terminal denials look the packet
5
5
  //! up there and in sets keyed on the link index, so a renamed or reused interface
6
6
  //! never matches an element of another link.
7
- use super::keys::{self, Key, Loads, ADDRESS, INDEX, NAME, PORT, PROTOCOL};
7
+ use super::keys::{
8
+ self, Field, Key, Loads, IIF, IIFNAME, IP_DADDR, IP_SADDR, L4PROTO, OIF, TH_DPORT, TH_SPORT,
9
+ };
8
10
  use super::*;
9
11
 
10
12
  /// Every endpoint link, index and name, and the absent interface (0, "") of
@@ -21,6 +23,14 @@ const SOURCE: &str = "private_source";
21
23
  const ANY: &str = "private_any";
22
24
  const PORTED: &str = "private_port";
23
25
 
26
+ /// The keys, as the incoming direction loads them where both directions do.
27
+ const LINK_KEY: [Field; 2] = [IIF, IIFNAME];
28
+ const INDEX_KEY: [Field; 1] = [IIF];
29
+ const NAME_KEY: [Field; 1] = [IIFNAME];
30
+ const SOURCE_KEY: [Field; 2] = [IIF, IP_SADDR];
31
+ const ANY_KEY: [Field; 4] = [IIF, OIF, IP_SADDR, IP_DADDR];
32
+ const PORTED_KEY: [Field; 7] = [IIF, OIF, IP_SADDR, IP_DADDR, L4PROTO, TH_SPORT, TH_DPORT];
33
+
24
34
  fn name(value: &str) -> Vec<u8> {
25
35
  let mut bytes = value.as_bytes().to_vec();
26
36
  bytes.resize(16, 0);
@@ -64,10 +74,10 @@ pub(super) fn sets(program: &mut Program<'_>, scope: &RouterScope) -> Result<()>
64
74
  );
65
75
  }
66
76
  }
67
- keys::exact(program, LINK, 32, INDEX + NAME, None, links)?;
68
- keys::exact(program, INDEX_SET, 33, INDEX, None, indices)?;
69
- keys::exact(program, NAME_SET, 34, NAME, None, names)?;
70
- keys::interval(program, SOURCE, 35, &[INDEX, ADDRESS], None, sources)?;
77
+ keys::exact(program, LINK, 32, &LINK_KEY, None, links)?;
78
+ keys::exact(program, INDEX_SET, 33, &INDEX_KEY, None, indices)?;
79
+ keys::exact(program, NAME_SET, 34, &NAME_KEY, None, names)?;
80
+ keys::interval(program, SOURCE, 35, &SOURCE_KEY, None, sources)?;
71
81
  let (mut any, mut ported) = (Vec::new(), Vec::new());
72
82
  for rule in &scope.rules {
73
83
  let key = Key::default()
@@ -86,22 +96,8 @@ pub(super) fn sets(program: &mut Program<'_>, scope: &RouterScope) -> Result<()>
86
96
  );
87
97
  }
88
98
  }
89
- keys::interval(
90
- program,
91
- ANY,
92
- 36,
93
- &[INDEX, INDEX, ADDRESS, ADDRESS],
94
- None,
95
- any,
96
- )?;
97
- keys::interval(
98
- program,
99
- PORTED,
100
- 37,
101
- &[INDEX, INDEX, ADDRESS, ADDRESS, PROTOCOL, PORT, PORT],
102
- None,
103
- ported,
104
- )?;
99
+ keys::interval(program, ANY, 36, &ANY_KEY, None, any)?;
100
+ keys::interval(program, PORTED, 37, &PORTED_KEY, None, ported)?;
105
101
  Ok(())
106
102
  }
107
103
  /// The directions a filter chain sees an endpoint link in.
@@ -114,8 +110,8 @@ fn directions(chain: &str) -> Vec<bool> {
114
110
  }
115
111
  /// The interface of one direction is an endpoint link or absent, by index and
116
112
  /// name.
117
- pub(super) fn known(incoming: bool) -> Vec<Attr> {
118
- Loads::new().link(incoming).lookup(LINK)
113
+ pub(super) fn known(incoming: bool) -> Result<Vec<Attr>> {
114
+ Loads::new().link(incoming).lookup(LINK, &LINK_KEY)
119
115
  }
120
116
  /// Anti-spoof guards: a packet arriving from an endpoint link must carry one of
121
117
  /// its source prefixes, and one leaving to it one of its prefixes as destination;
@@ -126,7 +122,7 @@ pub(super) fn guards(program: &mut Program<'_>, scope: &RouterScope, chain: &str
126
122
  return Ok(());
127
123
  }
128
124
  for incoming in directions(chain) {
129
- let mut foreign = known(incoming);
125
+ let mut foreign = known(incoming)?;
130
126
  foreign.push(expr("meta", vec![Attr::u32(1, 1), Attr::u32(2, 15)]));
131
127
  foreign.push(expr(
132
128
  "cmp",
@@ -138,12 +134,12 @@ pub(super) fn guards(program: &mut Program<'_>, scope: &RouterScope, chain: &str
138
134
  ));
139
135
  program.end(chain, foreign, 0)?;
140
136
  let mut spoofed = ipv4();
141
- spoofed.extend(known(incoming));
137
+ spoofed.extend(known(incoming)?);
142
138
  spoofed.extend(
143
139
  Loads::new()
144
140
  .index(incoming)
145
141
  .address(incoming)
146
- .absent(SOURCE),
142
+ .absent(SOURCE, &SOURCE_KEY)?,
147
143
  );
148
144
  program.end(chain, spoofed, 0)?;
149
145
  }
@@ -154,7 +150,7 @@ pub(super) fn guards(program: &mut Program<'_>, scope: &RouterScope, chain: &str
154
150
  /// protocol and ports. Input sees no outgoing and output no incoming interface,
155
151
  /// so each chain matches exactly the rules that were compiled into it.
156
152
  pub(super) fn grants(program: &mut Program<'_>, scope: &RouterScope, chain: &str) -> Result<()> {
157
- for (set, ported) in [(ANY, false), (PORTED, true)] {
153
+ for (set, key, ported) in [(ANY, &ANY_KEY[..], false), (PORTED, &PORTED_KEY[..], true)] {
158
154
  if !scope
159
155
  .rules
160
156
  .iter()
@@ -163,14 +159,14 @@ pub(super) fn grants(program: &mut Program<'_>, scope: &RouterScope, chain: &str
163
159
  continue;
164
160
  }
165
161
  let mut expressions = ipv4();
166
- expressions.extend(known(true));
167
- expressions.extend(known(false));
162
+ expressions.extend(known(true)?);
163
+ expressions.extend(known(false)?);
168
164
  let mut loads = Loads::new();
169
165
  loads.index(true).index(false).address(true).address(false);
170
166
  if ported {
171
167
  loads.protocol().port(true).port(false);
172
168
  }
173
- expressions.extend(loads.lookup(set));
169
+ expressions.extend(loads.lookup(set, key)?);
174
170
  program.end(chain, expressions, 1)?;
175
171
  }
176
172
  Ok(())
@@ -181,15 +177,14 @@ pub(super) fn denials(program: &mut Program<'_>, scope: &RouterScope, chain: &st
181
177
  return Ok(());
182
178
  }
183
179
  for incoming in directions(chain) {
184
- program.end(chain, Loads::new().index(incoming).lookup(INDEX_SET), 0)?;
185
180
  program.end(
186
181
  chain,
187
- Loads::new()
188
- .push(
189
- |at| super::hostgrant::meta_to(if incoming { 6 } else { 7 }, at),
190
- NAME,
191
- )
192
- .lookup(NAME_SET),
182
+ Loads::new().index(incoming).lookup(INDEX_SET, &INDEX_KEY)?,
183
+ 0,
184
+ )?;
185
+ program.end(
186
+ chain,
187
+ Loads::new().name(incoming).lookup(NAME_SET, &NAME_KEY)?,
193
188
  0,
194
189
  )?;
195
190
  }
@@ -6,8 +6,12 @@
6
6
  //! protocols, the live address and port, and the originally tracked address and
7
7
  //! port. A port field is an inclusive interval, so one element holds one port or
8
8
  //! a whole range, and every other field is exact.
9
- use super::hostgrant::{meta_to, register};
10
- use super::keys::{self, word, Key, Loads, ADDRESS, INDEX, NAME, PORT, PROTOCOL};
9
+ use super::hostgrant::register;
10
+ use super::keys::{
11
+ self, word, Field, Key, Loads, CT_ORIGINAL_DADDR, CT_ORIGINAL_DPORT, CT_ORIGINAL_SADDR,
12
+ CT_ORIGINAL_SPORT, CT_PROTOCOL, IIF, IIFNAME, IP_DADDR, IP_SADDR, L4PROTO, OIF, OIFNAME,
13
+ TH_DPORT, TH_SPORT,
14
+ };
11
15
  use super::*;
12
16
 
13
17
  /// Router handoff arrivals of a publication, classified before conntrack.
@@ -24,7 +28,39 @@ pub(super) const FORWARD: &str = "published_forward";
24
28
  /// the data is the source NAT to the outside address and port span.
25
29
  pub(super) const SYMMETRIC: &str = "published_symmetric";
26
30
 
27
- const LINK: [u32; 2] = [INDEX, NAME];
31
+ const ARRIVAL_KEY: [Field; 3] = [L4PROTO, IP_DADDR, TH_DPORT];
32
+ const ENDPOINT_KEY: [Field; 5] = [IIF, IIFNAME, L4PROTO, IP_SADDR, TH_SPORT];
33
+ /// The translation maps' key, and their data: the inside address and port, or
34
+ /// the inside address alone.
35
+ const TRANSLATION_KEY: [Field; 4] = [L4PROTO, CT_PROTOCOL, IP_DADDR, TH_DPORT];
36
+ const PORT_DATA: [Field; 2] = [IP_DADDR, TH_DPORT];
37
+ const ADDRESS_DATA: [Field; 1] = [IP_DADDR];
38
+ /// `[link, protocols, address, port, original address, original port]`, the key
39
+ /// of both admission sets, as their original direction loads it: inbound
40
+ /// toward the inside link and destination, symmetric from them.
41
+ const INBOUND_KEY: [Field; 8] = [
42
+ OIF,
43
+ OIFNAME,
44
+ L4PROTO,
45
+ CT_PROTOCOL,
46
+ IP_DADDR,
47
+ TH_DPORT,
48
+ CT_ORIGINAL_DADDR,
49
+ CT_ORIGINAL_DPORT,
50
+ ];
51
+ const SYMMETRIC_KEY: [Field; 8] = [
52
+ IIF,
53
+ IIFNAME,
54
+ L4PROTO,
55
+ CT_PROTOCOL,
56
+ IP_SADDR,
57
+ TH_SPORT,
58
+ CT_ORIGINAL_SADDR,
59
+ CT_ORIGINAL_SPORT,
60
+ ];
61
+ /// The source NAT a symmetric flow stores: the outside address and the first
62
+ /// and last port of its span.
63
+ const SYMMETRIC_DATA: [Field; 3] = [IP_SADDR, TH_SPORT, TH_SPORT];
28
64
 
29
65
  /// One publication on one hop. The outside is what clients reach (the uplink
30
66
  /// address and port on the host, the transit address and port in the router);
@@ -44,13 +80,6 @@ pub(super) struct Hop<'a> {
44
80
  pub(super) fixed: &'a LocalLink,
45
81
  }
46
82
 
47
- /// `[link, protocols, address, port, original address, original port]`, the key
48
- /// of both admission sets.
49
- fn flow_fields() -> Vec<u32> {
50
- let mut fields = LINK.to_vec();
51
- fields.extend([PROTOCOL, PROTOCOL, ADDRESS, PORT, ADDRESS, PORT]);
52
- fields
53
- }
54
83
  fn inbound_key(publication: &Publication<'_>) -> Result<IntervalElement> {
55
84
  let (outside, outside_first, outside_last) = publication.outside;
56
85
  let (inside, inside_first, inside_last) = publication.inside;
@@ -108,9 +137,6 @@ fn translation_loads() -> Loads {
108
137
  loads.protocols().address(false).port(false);
109
138
  loads
110
139
  }
111
- fn translation_fields() -> [u32; 4] {
112
- [PROTOCOL, PROTOCOL, ADDRESS, PORT]
113
- }
114
140
  fn ranged(publication: &Publication<'_>) -> bool {
115
141
  publication.outside.1 != publication.outside.2
116
142
  }
@@ -133,14 +159,7 @@ pub(super) fn sets(program: &mut Program<'_>, hop: &Hop<'_>, router: bool) -> Re
133
159
  .element(None),
134
160
  );
135
161
  }
136
- declare(
137
- program,
138
- ARRIVAL,
139
- 16,
140
- &[PROTOCOL, ADDRESS, PORT],
141
- None,
142
- arrivals,
143
- )?;
162
+ declare(program, ARRIVAL, 16, &ARRIVAL_KEY, None, arrivals)?;
144
163
  // Several publications may share inside ports that are not symmetric;
145
164
  // the classification is their union, as disjoint intervals.
146
165
  let mut endpoints = Vec::new();
@@ -155,9 +174,7 @@ pub(super) fn sets(program: &mut Program<'_>, hop: &Hop<'_>, router: bool) -> Re
155
174
  .element(None),
156
175
  );
157
176
  }
158
- let mut fields = LINK.to_vec();
159
- fields.extend([PROTOCOL, ADDRESS, PORT]);
160
- declare(program, ENDPOINT, 17, &fields, None, endpoints)?;
177
+ declare(program, ENDPOINT, 17, &ENDPOINT_KEY, None, endpoints)?;
161
178
  }
162
179
  let (mut ports, mut addresses) = (Vec::new(), Vec::new());
163
180
  for publication in &hop.publications {
@@ -175,13 +192,20 @@ pub(super) fn sets(program: &mut Program<'_>, hop: &Hop<'_>, router: bool) -> Re
175
192
  ports.push(key.element(Some(data)));
176
193
  }
177
194
  }
178
- declare(program, PORT_MAP, 18, &translation_fields(), Some(8), ports)?;
195
+ declare(
196
+ program,
197
+ PORT_MAP,
198
+ 18,
199
+ &TRANSLATION_KEY,
200
+ Some(&PORT_DATA),
201
+ ports,
202
+ )?;
179
203
  declare(
180
204
  program,
181
205
  ADDRESS_MAP,
182
206
  19,
183
- &translation_fields(),
184
- Some(4),
207
+ &TRANSLATION_KEY,
208
+ Some(&ADDRESS_DATA),
185
209
  addresses,
186
210
  )?;
187
211
  let inbound = hop
@@ -189,21 +213,28 @@ pub(super) fn sets(program: &mut Program<'_>, hop: &Hop<'_>, router: bool) -> Re
189
213
  .iter()
190
214
  .map(inbound_key)
191
215
  .collect::<Result<Vec<_>>>()?;
192
- declare(program, FORWARD, 20, &flow_fields(), None, inbound)?;
216
+ declare(program, FORWARD, 20, &INBOUND_KEY, None, inbound)?;
193
217
  let symmetric = hop
194
218
  .publications
195
219
  .iter()
196
220
  .filter(|publication| publication.symmetric)
197
221
  .map(symmetric_key)
198
222
  .collect::<Result<Vec<_>>>()?;
199
- declare(program, SYMMETRIC, 21, &flow_fields(), Some(12), symmetric)
223
+ declare(
224
+ program,
225
+ SYMMETRIC,
226
+ 21,
227
+ &SYMMETRIC_KEY,
228
+ Some(&SYMMETRIC_DATA),
229
+ symmetric,
230
+ )
200
231
  }
201
232
  fn declare(
202
233
  program: &mut Program<'_>,
203
234
  name: &str,
204
235
  id: u32,
205
- fields: &[u32],
206
- data: Option<u32>,
236
+ fields: &[Field],
237
+ data: Option<&[Field]>,
207
238
  elements: Vec<IntervalElement>,
208
239
  ) -> Result<()> {
209
240
  keys::interval(program, name, id, fields, data, elements).map(|_| ())
@@ -217,6 +248,7 @@ fn admissions(
217
248
  program: &mut Program<'_>,
218
249
  hop: &Hop<'_>,
219
250
  set: &str,
251
+ key: &[Field],
220
252
  protocols: &BTreeSet<&str>,
221
253
  fixed_incoming: bool,
222
254
  loads: fn(bool) -> Loads,
@@ -233,7 +265,7 @@ fn admissions(
233
265
  result
234
266
  };
235
267
  let mut established = envelope(false, 2);
236
- established.extend(loads(false).lookup(set));
268
+ established.extend(loads(false).lookup(set, key)?);
237
269
  program.end("forward", established, 1)?;
238
270
  for transport in protocols {
239
271
  let mut opening = envelope(false, 8);
@@ -241,11 +273,11 @@ fn admissions(
241
273
  if *transport == "tcp" {
242
274
  opening.extend(opening_tcp());
243
275
  }
244
- opening.extend(loads(false).lookup(set));
276
+ opening.extend(loads(false).lookup(set, key)?);
245
277
  program.end("forward", opening, 1)?;
246
278
  }
247
279
  let mut reply = envelope(true, 2);
248
- reply.extend(loads(true).lookup(set));
280
+ reply.extend(loads(true).lookup(set, key)?);
249
281
  program.end("forward", reply, 1)
250
282
  }
251
283
  fn protocols<'a>(hop: &Hop<'a>, symmetric: bool) -> BTreeSet<&'a str> {
@@ -262,6 +294,7 @@ pub(super) fn inbound(program: &mut Program<'_>, hop: &Hop<'_>) -> Result<()> {
262
294
  program,
263
295
  hop,
264
296
  FORWARD,
297
+ &INBOUND_KEY,
265
298
  &protocols(hop, false),
266
299
  true,
267
300
  inbound_loads,
@@ -275,6 +308,7 @@ pub(super) fn symmetric(program: &mut Program<'_>, hop: &Hop<'_>) -> Result<()>
275
308
  program,
276
309
  hop,
277
310
  SYMMETRIC,
311
+ &SYMMETRIC_KEY,
278
312
  &protocols(hop, true),
279
313
  false,
280
314
  symmetric_loads,
@@ -317,7 +351,7 @@ pub(super) fn translations(program: &mut Program<'_>, hop: &Hop<'_>) -> Result<(
317
351
  ] {
318
352
  if present {
319
353
  let mut expressions = tracked(hop.fixed, true);
320
- expressions.extend(translation_loads().map(set));
354
+ expressions.extend(translation_loads().map(set, &TRANSLATION_KEY)?);
321
355
  expressions.push(nat(1, ports));
322
356
  program.rule("pre", expressions)?;
323
357
  }
@@ -329,7 +363,7 @@ pub(super) fn translations(program: &mut Program<'_>, hop: &Hop<'_>) -> Result<(
329
363
  pub(super) fn symmetric_translation(program: &mut Program<'_>, hop: &Hop<'_>) -> Result<()> {
330
364
  if hop.publications.iter().any(|item| item.symmetric) {
331
365
  let mut expressions = tracked(hop.fixed, false);
332
- expressions.extend(symmetric_loads(false).map(SYMMETRIC));
366
+ expressions.extend(symmetric_loads(false).map(SYMMETRIC, &SYMMETRIC_KEY)?);
333
367
  expressions.push(nat(0, 2));
334
368
  program.rule("post", expressions)?;
335
369
  }
@@ -343,11 +377,8 @@ pub(super) fn arrival(program: &mut Program<'_>, hop: &Hop<'_>) -> Result<()> {
343
377
  let mut expressions = ipv4();
344
378
  expressions.extend(link(hop.fixed, true));
345
379
  let mut loads = Loads::new();
346
- loads
347
- .push(|index| meta_to(16, index), PROTOCOL)
348
- .address(false)
349
- .port(false);
350
- expressions.extend(loads.lookup(ARRIVAL));
380
+ loads.protocol().address(false).port(false);
381
+ expressions.extend(loads.lookup(ARRIVAL, &ARRIVAL_KEY)?);
351
382
  program.end("raw_return", expressions, 1)?;
352
383
  }
353
384
  Ok(())
@@ -358,12 +389,8 @@ pub(super) fn endpoint(program: &mut Program<'_>, hop: &Hop<'_>) -> Result<()> {
358
389
  if !hop.publications.is_empty() {
359
390
  let mut expressions = ipv4();
360
391
  let mut loads = Loads::new();
361
- loads
362
- .link(true)
363
- .push(|index| meta_to(16, index), PROTOCOL)
364
- .address(true)
365
- .port(true);
366
- expressions.extend(loads.lookup(ENDPOINT));
392
+ loads.link(true).protocol().address(true).port(true);
393
+ expressions.extend(loads.lookup(ENDPOINT, &ENDPOINT_KEY)?);
367
394
  program.end("raw_in", expressions, 1)?;
368
395
  }
369
396
  Ok(())