@push.rocks/smartnftables 4.3.1 → 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.
@@ -1,10 +1,309 @@
1
1
  //! Concatenated set keys shared by every set-backed part of the compiled graph:
2
- //! the loads that build a key in consecutive 32-bit registers, element bounds
3
- //! built field by field, the disjoint union the kernel's interval backend needs,
4
- //! and set declarations that are never created empty.
2
+ //! the fields a key is built from, the loads that build it in consecutive 32-bit
3
+ //! registers, element bounds built field by field, the disjoint union the
4
+ //! kernel's interval backend needs, and set declarations that are never created
5
+ //! empty.
6
+ //!
7
+ //! A [`Field`] is the single source of a key field. Its loads fill the registers
8
+ //! a lookup reads, and a set declared on it carries the types nft needs to list
9
+ //! the set and every rule that looks it up: the key and data types, their byte
10
+ //! orders and the `typeof` expressions, exactly as nftables 1.0.9 writes them.
5
11
  use super::hostgrant::{ct_to, lookup, meta_to, payload_to, register};
6
12
  use super::*;
7
13
 
14
+ /// The expression that loads a field, which is also its nft `typeof`.
15
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
16
+ enum Source {
17
+ /// A meta key (`NFT_META_*`).
18
+ Meta(u32),
19
+ /// The header field at `offset` from `base` (1 network, 2 transport), with
20
+ /// nft's protocol description (`enum proto_desc_id`) and header template.
21
+ Payload {
22
+ base: u32,
23
+ offset: u32,
24
+ description: u32,
25
+ template: u32,
26
+ },
27
+ /// A conntrack key, of the original (0) or reply (1) tuple, or of neither.
28
+ Ct { key: u32, direction: Option<u8> },
29
+ }
30
+
31
+ /// One key or data field: its value's length and nft's description of it.
32
+ #[derive(Clone, Copy, Debug, PartialEq, Eq)]
33
+ pub(super) struct Field {
34
+ /// The value's bytes. A key pads every field to whole 32-bit registers.
35
+ pub(super) length: u32,
36
+ /// nft's datatype (`enum datatypes`).
37
+ datatype: u32,
38
+ /// nft's byte order of the value: host (1) or big endian (2).
39
+ byteorder: u32,
40
+ source: Source,
41
+ /// nft's spelling of the expression, as `typeof` names it.
42
+ pub(super) name: &'static str,
43
+ }
44
+
45
+ const HOST: u32 = 1;
46
+ const BIG: u32 = 2;
47
+ const IFINDEX: u32 = 20;
48
+ const IFNAME: u32 = 41;
49
+ const INET_PROTOCOL: u32 = 12;
50
+ const IPV4_ADDRESS: u32 = 7;
51
+ const INET_SERVICE: u32 = 13;
52
+ const INTEGER: u32 = 4;
53
+ const CT_LABEL_TYPE: u32 = 30;
54
+ /// nft's protocol descriptions of the IPv4 header and of the transport header
55
+ /// (`th`), whose templates number the fields from 1.
56
+ const IP_HEADER: u32 = 12;
57
+ const TRANSPORT_HEADER: u32 = 11;
58
+
59
+ const fn meta_field(key: u32, length: u32, datatype: u32, name: &'static str) -> Field {
60
+ Field {
61
+ length,
62
+ datatype,
63
+ byteorder: HOST,
64
+ source: Source::Meta(key),
65
+ name,
66
+ }
67
+ }
68
+ const fn address_field(offset: u32, template: u32, name: &'static str) -> Field {
69
+ Field {
70
+ length: ADDRESS,
71
+ datatype: IPV4_ADDRESS,
72
+ byteorder: BIG,
73
+ source: Source::Payload {
74
+ base: 1,
75
+ offset,
76
+ description: IP_HEADER,
77
+ template,
78
+ },
79
+ name,
80
+ }
81
+ }
82
+ const fn port_field(offset: u32, template: u32, name: &'static str) -> Field {
83
+ Field {
84
+ length: PORT,
85
+ datatype: INET_SERVICE,
86
+ byteorder: BIG,
87
+ source: Source::Payload {
88
+ base: 2,
89
+ offset,
90
+ description: TRANSPORT_HEADER,
91
+ template,
92
+ },
93
+ name,
94
+ }
95
+ }
96
+ const fn ct_field(
97
+ key: u32,
98
+ direction: Option<u8>,
99
+ length: u32,
100
+ datatype: u32,
101
+ byteorder: u32,
102
+ name: &'static str,
103
+ ) -> Field {
104
+ Field {
105
+ length,
106
+ datatype,
107
+ byteorder,
108
+ source: Source::Ct { key, direction },
109
+ name,
110
+ }
111
+ }
112
+
113
+ pub(super) const IIF: Field = meta_field(4, INDEX, IFINDEX, "meta iif");
114
+ pub(super) const OIF: Field = meta_field(5, INDEX, IFINDEX, "meta oif");
115
+ pub(super) const IIFNAME: Field = meta_field(6, NAME, IFNAME, "meta iifname");
116
+ pub(super) const OIFNAME: Field = meta_field(7, NAME, IFNAME, "meta oifname");
117
+ pub(super) const L4PROTO: Field = meta_field(16, PROTOCOL, INET_PROTOCOL, "meta l4proto");
118
+ pub(super) const CT_PROTOCOL: Field =
119
+ ct_field(10, None, PROTOCOL, INET_PROTOCOL, BIG, "ct protocol");
120
+ pub(super) const IP_SADDR: Field = address_field(12, 11, "ip saddr");
121
+ pub(super) const IP_DADDR: Field = address_field(16, 12, "ip daddr");
122
+ pub(super) const TH_SPORT: Field = port_field(0, 1, "th sport");
123
+ pub(super) const TH_DPORT: Field = port_field(2, 2, "th dport");
124
+ pub(super) const CT_ORIGINAL_SADDR: Field = ct_field(
125
+ 19,
126
+ Some(0),
127
+ ADDRESS,
128
+ IPV4_ADDRESS,
129
+ BIG,
130
+ "ct original ip saddr",
131
+ );
132
+ pub(super) const CT_ORIGINAL_DADDR: Field = ct_field(
133
+ 20,
134
+ Some(0),
135
+ ADDRESS,
136
+ IPV4_ADDRESS,
137
+ BIG,
138
+ "ct original ip daddr",
139
+ );
140
+ pub(super) const CT_ORIGINAL_SPORT: Field = ct_field(
141
+ 11,
142
+ Some(0),
143
+ PORT,
144
+ INET_SERVICE,
145
+ BIG,
146
+ "ct original proto-src",
147
+ );
148
+ pub(super) const CT_ORIGINAL_DPORT: Field = ct_field(
149
+ 12,
150
+ Some(0),
151
+ PORT,
152
+ INET_SERVICE,
153
+ BIG,
154
+ "ct original proto-dst",
155
+ );
156
+ pub(super) const CT_REPLY_SADDR: Field =
157
+ ct_field(19, Some(1), ADDRESS, IPV4_ADDRESS, BIG, "ct reply ip saddr");
158
+ pub(super) const CT_REPLY_SPORT: Field =
159
+ ct_field(11, Some(1), PORT, INET_SERVICE, BIG, "ct reply proto-src");
160
+ pub(super) const CT_ZONE: Field = ct_field(17, None, ZONE, INTEGER, HOST, "ct zone");
161
+ pub(super) const CT_LABEL: Field = ct_field(13, None, LABEL, CT_LABEL_TYPE, HOST, "ct label");
162
+
163
+ /// nft's expression types (`enum expr_types`) in set userdata.
164
+ const PAYLOAD_EXPRESSION: u32 = 7;
165
+ const META_EXPRESSION: u32 = 9;
166
+ const CT_EXPRESSION: u32 = 12;
167
+ const CONCAT_EXPRESSION: u32 = 13;
168
+ /// nft 1.0.9 describes at most this many concatenated fields by `typeof`: its
169
+ /// concatenation userdata is bounded by `NFT_REG32_SIZE` (4) where the register
170
+ /// count was meant. A longer key is described by its type alone, as nft does
171
+ /// for a set declared by `type`.
172
+ const TYPEOF_FIELDS: usize = 4;
173
+ /// Bits of one field's type within a concatenation's type; nft keeps the type
174
+ /// in 32 bits and drops what a long concatenation shifts out.
175
+ const TYPE_BITS: u32 = 6;
176
+
177
+ impl Field {
178
+ /// The register-padded bytes the field occupies in a key.
179
+ pub(super) fn padded(self) -> u32 {
180
+ self.length.div_ceil(4) * 4
181
+ }
182
+ /// The load of this field into the 32-bit register `index`.
183
+ pub(super) fn load(self, index: u32) -> Attr {
184
+ match self.source {
185
+ Source::Meta(key) => meta_to(key, index),
186
+ Source::Payload { base, offset, .. } => payload_to(base, offset, self.length, index),
187
+ Source::Ct { key, direction } => ct_to(key, direction, index),
188
+ }
189
+ }
190
+ /// Whether a lookup may load `self` where a set declares `declared`: the
191
+ /// same length and type, as when one direction's field stands for the other.
192
+ fn fits(self, declared: Field) -> bool {
193
+ self.length == declared.length && self.datatype == declared.datatype
194
+ }
195
+ /// nft's expression type and its userdata (`build_udata`).
196
+ fn expression(self) -> Result<(u32, Vec<u8>)> {
197
+ let mut data = Vec::new();
198
+ let kind = match self.source {
199
+ Source::Meta(key) => {
200
+ tlv_number(&mut data, 0, key)?;
201
+ META_EXPRESSION
202
+ }
203
+ Source::Payload {
204
+ description,
205
+ template,
206
+ ..
207
+ } => {
208
+ tlv_number(&mut data, 0, description)?;
209
+ tlv_number(&mut data, 1, template)?;
210
+ PAYLOAD_EXPRESSION
211
+ }
212
+ Source::Ct { key, direction } => {
213
+ tlv_number(&mut data, 0, key)?;
214
+ // nft stores the direction as a signed byte: none is -1.
215
+ tlv_number(&mut data, 1, direction.map_or(u32::MAX, u32::from))?;
216
+ CT_EXPRESSION
217
+ }
218
+ };
219
+ Ok((kind, data))
220
+ }
221
+ }
222
+
223
+ /// One userdata attribute: a type byte, a length byte and the value.
224
+ fn tlv(out: &mut Vec<u8>, kind: u8, value: &[u8]) -> Result<()> {
225
+ out.push(kind);
226
+ out.push(u8::try_from(value.len()).map_err(|_| Error::Invalid)?);
227
+ out.extend_from_slice(value);
228
+ Ok(())
229
+ }
230
+ /// libnftnl writes userdata numbers in host byte order.
231
+ fn tlv_number(out: &mut Vec<u8>, kind: u8, value: u32) -> Result<()> {
232
+ tlv(out, kind, &value.to_ne_bytes())
233
+ }
234
+
235
+ /// The bytes of a key: every field padded to whole registers.
236
+ pub(super) fn key_length(fields: &[Field]) -> u32 {
237
+ fields.iter().map(|field| field.padded()).sum()
238
+ }
239
+ /// The bytes of map data: one field as it is, a concatenation padded.
240
+ pub(super) fn data_length(fields: &[Field]) -> u32 {
241
+ match fields {
242
+ [field] => field.length,
243
+ _ => key_length(fields),
244
+ }
245
+ }
246
+ /// nft's type of a key or data: a field's own, or a concatenation's, each
247
+ /// field's type in turn.
248
+ pub(super) fn datatype(fields: &[Field]) -> u32 {
249
+ match fields {
250
+ [field] => field.datatype,
251
+ _ => fields
252
+ .iter()
253
+ .fold(0, |result, field| (result << TYPE_BITS) | field.datatype),
254
+ }
255
+ }
256
+ /// nft's byte order of a key or data: a field's own; a concatenation has none.
257
+ fn byteorder(fields: &[Field]) -> u32 {
258
+ match fields {
259
+ [field] => field.byteorder,
260
+ _ => 0,
261
+ }
262
+ }
263
+ /// nft's `typeof` description of a key or data (`set_key_expression`), or none
264
+ /// for a concatenation longer than nft describes.
265
+ fn typeof_description(fields: &[Field]) -> Result<Option<Vec<u8>>> {
266
+ let (kind, data) = match fields {
267
+ [field] => field.expression()?,
268
+ _ if fields.len() <= TYPEOF_FIELDS => {
269
+ let mut data = Vec::new();
270
+ for (position, field) in (0_u8..).zip(fields) {
271
+ let (kind, description) = field.expression()?;
272
+ let mut nested = Vec::new();
273
+ tlv_number(&mut nested, 0, kind)?;
274
+ tlv(&mut nested, 1, &description)?;
275
+ tlv(&mut data, position, &nested)?;
276
+ }
277
+ (CONCAT_EXPRESSION, data)
278
+ }
279
+ _ => return Ok(None),
280
+ };
281
+ let mut result = Vec::new();
282
+ tlv_number(&mut result, 0, kind)?;
283
+ tlv(&mut result, 1, &data)?;
284
+ Ok(Some(result))
285
+ }
286
+ /// The set userdata nft writes (`mnl_nft_set_add`): the key's byte order, a
287
+ /// map's data byte order, the key's and a map's data `typeof`, and for a map
288
+ /// that its data is no interval.
289
+ pub(super) fn userdata(key: &[Field], data: Option<&[Field]>) -> Result<Vec<u8>> {
290
+ let mut result = Vec::new();
291
+ tlv_number(&mut result, 0, byteorder(key))?;
292
+ if let Some(data) = data {
293
+ tlv_number(&mut result, 1, byteorder(data))?;
294
+ }
295
+ if let Some(description) = typeof_description(key)? {
296
+ tlv(&mut result, 3, &description)?;
297
+ }
298
+ if let Some(data) = data {
299
+ if let Some(description) = typeof_description(data)? {
300
+ tlv(&mut result, 4, &description)?;
301
+ }
302
+ tlv_number(&mut result, 6, 0)?;
303
+ }
304
+ Ok(result)
305
+ }
306
+
8
307
  /// Field byte lengths. Each field occupies whole 32-bit registers of the key.
9
308
  pub(super) const INDEX: u32 = 4;
10
309
  pub(super) const NAME: u32 = 16;
@@ -16,88 +315,116 @@ pub(super) const LABEL: u32 = 16;
16
315
 
17
316
  /// Key loads into consecutive 32-bit registers, starting where a lookup reads.
18
317
  pub(super) struct Loads {
19
- expressions: Vec<Attr>,
20
- index: u32,
318
+ fields: Vec<Field>,
21
319
  }
22
320
  impl Loads {
23
321
  pub(super) fn new() -> Self {
24
- Self {
25
- expressions: Vec::new(),
26
- index: 4,
27
- }
322
+ Self { fields: Vec::new() }
28
323
  }
29
- pub(super) fn push(&mut self, load: impl FnOnce(u32) -> Attr, length: u32) -> &mut Self {
30
- self.expressions.push(load(self.index));
31
- self.index += length.div_ceil(4);
324
+ fn push(&mut self, field: Field) -> &mut Self {
325
+ self.fields.push(field);
32
326
  self
33
327
  }
34
328
  /// An interface index; an absent interface loads as index 0.
35
329
  pub(super) fn index(&mut self, incoming: bool) -> &mut Self {
36
- self.push(|at| meta_to(if incoming { 4 } else { 5 }, at), INDEX)
330
+ self.push(if incoming { IIF } else { OIF })
37
331
  }
38
- /// An interface index and name; an absent interface loads as 0 and "".
332
+ /// An interface name; an absent interface loads as "".
333
+ pub(super) fn name(&mut self, incoming: bool) -> &mut Self {
334
+ self.push(if incoming { IIFNAME } else { OIFNAME })
335
+ }
336
+ /// An interface index and name.
39
337
  pub(super) fn link(&mut self, incoming: bool) -> &mut Self {
40
- self.index(incoming)
41
- .push(|at| meta_to(if incoming { 6 } else { 7 }, at), NAME)
338
+ self.index(incoming).name(incoming)
42
339
  }
43
340
  pub(super) fn protocol(&mut self) -> &mut Self {
44
- self.push(|at| meta_to(16, at), PROTOCOL)
341
+ self.push(L4PROTO)
45
342
  }
46
343
  pub(super) fn protocols(&mut self) -> &mut Self {
47
- self.protocol().push(|at| ct_to(10, false, at), PROTOCOL)
344
+ self.protocol().push(CT_PROTOCOL)
48
345
  }
49
346
  pub(super) fn address(&mut self, source: bool) -> &mut Self {
50
- self.push(
51
- |at| payload_to(1, if source { 12 } else { 16 }, 4, at),
52
- ADDRESS,
53
- )
347
+ self.push(if source { IP_SADDR } else { IP_DADDR })
54
348
  }
55
349
  pub(super) fn port(&mut self, source: bool) -> &mut Self {
56
- self.push(|at| payload_to(2, if source { 0 } else { 2 }, 2, at), PORT)
350
+ self.push(if source { TH_SPORT } else { TH_DPORT })
57
351
  }
58
352
  /// The originally tracked address and port of one side.
59
353
  pub(super) fn original(&mut self, source: bool) -> &mut Self {
60
354
  self.original_address(source).original_port(source)
61
355
  }
62
356
  pub(super) fn original_address(&mut self, source: bool) -> &mut Self {
63
- self.push(|at| ct_to(if source { 19 } else { 20 }, true, at), ADDRESS)
357
+ self.push(if source {
358
+ CT_ORIGINAL_SADDR
359
+ } else {
360
+ CT_ORIGINAL_DADDR
361
+ })
64
362
  }
65
363
  pub(super) fn original_port(&mut self, source: bool) -> &mut Self {
66
- self.push(|at| ct_to(if source { 11 } else { 12 }, true, at), PORT)
364
+ self.push(if source {
365
+ CT_ORIGINAL_SPORT
366
+ } else {
367
+ CT_ORIGINAL_DPORT
368
+ })
369
+ }
370
+ /// The reply tuple's source address and port: where a translated flow was
371
+ /// translated to, whichever direction the packet travels.
372
+ pub(super) fn translated(&mut self) -> &mut Self {
373
+ self.push(CT_REPLY_SADDR).push(CT_REPLY_SPORT)
67
374
  }
68
375
  pub(super) fn zone(&mut self) -> &mut Self {
69
- self.push(|at| ct_to(17, false, at), ZONE)
376
+ self.push(CT_ZONE)
70
377
  }
71
378
  pub(super) fn label(&mut self) -> &mut Self {
72
- self.push(|at| ct_to(13, false, at), LABEL)
73
- }
74
- fn finish(&mut self, set: &str, data: bool, invert: bool) -> Vec<Attr> {
75
- let mut result = std::mem::take(&mut self.expressions);
76
- self.index = 4;
379
+ self.push(CT_LABEL)
380
+ }
381
+ /// The loads, then the lookup in `set`, whose key `declared` the loads must
382
+ /// fit field by field.
383
+ fn finish(
384
+ &mut self,
385
+ set: &str,
386
+ declared: &[Field],
387
+ data: bool,
388
+ invert: bool,
389
+ ) -> Result<Vec<Attr>> {
390
+ let fields = std::mem::take(&mut self.fields);
391
+ require(
392
+ fields.len() == declared.len()
393
+ && fields
394
+ .iter()
395
+ .zip(declared)
396
+ .all(|(field, declared)| field.fits(*declared)),
397
+ )?;
398
+ let mut result = Vec::new();
399
+ let mut index = 4;
400
+ for field in fields {
401
+ result.push(field.load(index));
402
+ index += field.length.div_ceil(4);
403
+ }
77
404
  let mut expression = lookup(set);
78
405
  if data || invert {
79
- let mut fields = vec![Attr::string(1, set), Attr::u32(2, register(4))];
406
+ let mut attributes = vec![Attr::string(1, set), Attr::u32(2, register(4))];
80
407
  if data {
81
408
  // The map's data replaces the key from the first register on.
82
- fields.push(Attr::u32(3, register(4)));
409
+ attributes.push(Attr::u32(3, register(4)));
83
410
  }
84
- fields.push(Attr::u32(5, u32::from(invert)));
85
- expression = expr("lookup", fields);
411
+ attributes.push(Attr::u32(5, u32::from(invert)));
412
+ expression = expr("lookup", attributes);
86
413
  }
87
414
  result.push(expression);
88
- result
415
+ Ok(result)
89
416
  }
90
417
  /// Membership of the loaded key.
91
- pub(super) fn lookup(&mut self, set: &str) -> Vec<Attr> {
92
- self.finish(set, false, false)
418
+ pub(super) fn lookup(&mut self, set: &str, declared: &[Field]) -> Result<Vec<Attr>> {
419
+ self.finish(set, declared, false, false)
93
420
  }
94
421
  /// A map lookup storing the element's data from the first key register on.
95
- pub(super) fn map(&mut self, set: &str) -> Vec<Attr> {
96
- self.finish(set, true, false)
422
+ pub(super) fn map(&mut self, set: &str, declared: &[Field]) -> Result<Vec<Attr>> {
423
+ self.finish(set, declared, true, false)
97
424
  }
98
425
  /// Absence of the loaded key.
99
- pub(super) fn absent(&mut self, set: &str) -> Vec<Attr> {
100
- self.finish(set, false, true)
426
+ pub(super) fn absent(&mut self, set: &str, declared: &[Field]) -> Result<Vec<Attr>> {
427
+ self.finish(set, declared, false, true)
101
428
  }
102
429
  }
103
430
  /// Stores `length` bytes of map data from the first key register into a
@@ -174,13 +501,13 @@ impl Key {
174
501
  }
175
502
 
176
503
  /// The byte range of each field inside the padded key.
177
- fn layout(fields: &[u32]) -> Vec<std::ops::Range<usize>> {
504
+ fn layout(fields: &[Field]) -> Vec<std::ops::Range<usize>> {
178
505
  let mut at = 0;
179
506
  fields
180
507
  .iter()
181
- .map(|length| {
182
- let range = at..at + *length as usize;
183
- at += length.div_ceil(4) as usize * 4;
508
+ .map(|field| {
509
+ let range = at..at + field.length as usize;
510
+ at += field.padded() as usize;
184
511
  range
185
512
  })
186
513
  .collect()
@@ -240,7 +567,7 @@ fn subtract(
240
567
  /// of a map may overlap only where they carry the same data; anything else is a
241
568
  /// policy the compiler cannot express and rejects.
242
569
  pub(super) fn disjoint(
243
- fields: &[u32],
570
+ fields: &[Field],
244
571
  mut elements: Vec<IntervalElement>,
245
572
  ) -> Result<Vec<IntervalElement>> {
246
573
  let layout = layout(fields);
@@ -272,8 +599,8 @@ pub(super) fn interval(
272
599
  program: &mut Program<'_>,
273
600
  name: &str,
274
601
  id: u32,
275
- fields: &[u32],
276
- data: Option<u32>,
602
+ fields: &[Field],
603
+ data: Option<&[Field]>,
277
604
  elements: Vec<IntervalElement>,
278
605
  ) -> Result<bool> {
279
606
  if elements.is_empty() {
@@ -289,8 +616,8 @@ pub(super) fn exact(
289
616
  program: &mut Program<'_>,
290
617
  name: &str,
291
618
  id: u32,
292
- key_length: u32,
293
- data: Option<u32>,
619
+ key: &[Field],
620
+ data: Option<&[Field]>,
294
621
  mut elements: Vec<(Vec<u8>, Option<Vec<u8>>)>,
295
622
  ) -> Result<bool> {
296
623
  if elements.is_empty() {
@@ -299,7 +626,11 @@ pub(super) fn exact(
299
626
  elements.sort();
300
627
  elements.dedup();
301
628
  require(elements.windows(2).all(|pair| pair[0].0 != pair[1].0))?;
302
- program.exact_set(name, id, key_length, data)?;
629
+ program.exact_set(name, id, key, data)?;
303
630
  program.exact_elements(name, elements)?;
304
631
  Ok(true)
305
632
  }
633
+
634
+ #[cfg(test)]
635
+ #[path = "egress_keys_tests.rs"]
636
+ mod tests;