@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.
- package/changelog.md +17 -0
- package/dist_rust/smartnftables_linux_amd64_musl +0 -0
- package/dist_rust/smartnftables_linux_amd64_musl.tsrust-build.json +5 -5
- package/dist_rust/smartnftables_linux_arm64_musl +0 -0
- package/dist_rust/smartnftables_linux_arm64_musl.tsrust-build.json +5 -5
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/managed.egress.types.d.ts +25 -4
- package/package.json +4 -4
- package/readme.md +89 -2
- package/rust/src/egress.compile.rs +105 -38
- package/rust/src/egress.hostgrant.rs +74 -34
- package/rust/src/egress.keys.rs +383 -52
- package/rust/src/egress.leased.rs +71 -42
- package/rust/src/egress.poolguard.rs +72 -0
- package/rust/src/egress.private.rs +32 -37
- package/rust/src/egress.published.rs +74 -47
- package/rust/src/egress.router.rs +1 -1
- package/rust/src/egress.rs +65 -1
- package/rust/src/egress.workloadgrant.rs +30 -28
- package/rust/src/egress_hostgrant_tests.rs +12 -0
- package/rust/src/egress_keys_tests.rs +150 -0
- package/rust/src/egress_poolguardpublished_tests.rs +418 -0
- package/rust/src/egress_tests.rs +47 -4
- package/rust/src/owner.rs +96 -28
- package/rust/src/owner_listing_tests.rs +634 -0
- package/rust/src/owner_tests.rs +3 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/managed.egress.types.ts +26 -4
package/rust/src/owner.rs
CHANGED
|
@@ -302,7 +302,13 @@ impl Owner {
|
|
|
302
302
|
}
|
|
303
303
|
Ok(())
|
|
304
304
|
}
|
|
305
|
+
/// Whether `graph` is `prepared`'s compiled graph. LEGACY(<=4.4.x): this
|
|
306
|
+
/// includes the untyped sets of a release before typed sets; with the next
|
|
307
|
+
/// major it becomes exact agreement alone.
|
|
305
308
|
fn matches(&self, graph: &Graph, prepared: &Prepared) -> Result<bool> {
|
|
309
|
+
Ok(self.agreement(graph, prepared)? != Agreement::Different)
|
|
310
|
+
}
|
|
311
|
+
fn agreement(&self, graph: &Graph, prepared: &Prepared) -> Result<Agreement> {
|
|
306
312
|
let program = prepared.program(&self.identity.table_name)?;
|
|
307
313
|
let expected_chains: Vec<_> = program
|
|
308
314
|
.iter()
|
|
@@ -336,21 +342,8 @@ impl Owner {
|
|
|
336
342
|
}
|
|
337
343
|
result
|
|
338
344
|
};
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
for (kind, attrs) in &program {
|
|
342
|
-
match kind {
|
|
343
|
-
9 => {
|
|
344
|
-
expected_sets.insert(wire::text(attrs, 2)?, (set_identity(attrs)?, Vec::new()));
|
|
345
|
-
}
|
|
346
|
-
12 => {
|
|
347
|
-
let entry = expected_sets
|
|
348
|
-
.get_mut(&wire::text(attrs, 2)?)
|
|
349
|
-
.ok_or(Error::Invalid)?;
|
|
350
|
-
entry.1.extend(element_keys(&wire::attrs(&wire::one(attrs, 3)?.value)?)?);
|
|
351
|
-
}
|
|
352
|
-
_ => {}
|
|
353
|
-
}
|
|
345
|
+
if expected_chains != actual_chains || regroup(expected_rules) != regroup(actual_rules) {
|
|
346
|
+
return Ok(Agreement::Different);
|
|
354
347
|
}
|
|
355
348
|
let mut actual_sets = std::collections::BTreeMap::new();
|
|
356
349
|
for (attrs, keys) in &graph.sets {
|
|
@@ -361,12 +354,22 @@ impl Owner {
|
|
|
361
354
|
return Err(Error::Conflict);
|
|
362
355
|
}
|
|
363
356
|
}
|
|
364
|
-
for entry in
|
|
357
|
+
for entry in actual_sets.values_mut() {
|
|
365
358
|
entry.1.sort();
|
|
366
359
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
360
|
+
if expected_sets(&program, false)? == actual_sets {
|
|
361
|
+
return Ok(Agreement::Exact);
|
|
362
|
+
}
|
|
363
|
+
// LEGACY(<=4.4.x): kernel state applied before typed sets carries every
|
|
364
|
+
// set with key and data type 0 and no userdata, which Linux stores and
|
|
365
|
+
// dumps but never interprets, so it enforces exactly the same graph.
|
|
366
|
+
// Such a graph is adopted, inspected and released as this graph, and
|
|
367
|
+
// reconciliation always replaces it. Remove with the next major.
|
|
368
|
+
Ok(if expected_sets(&program, true)? == actual_sets {
|
|
369
|
+
Agreement::Untyped
|
|
370
|
+
} else {
|
|
371
|
+
Agreement::Different
|
|
372
|
+
})
|
|
370
373
|
}
|
|
371
374
|
fn adopt(&mut self, graph: Graph, transition: &Transition) -> Result<Graph> {
|
|
372
375
|
self.binding(&graph, transition.previous.as_ref())?;
|
|
@@ -464,13 +467,18 @@ impl Owner {
|
|
|
464
467
|
let mut operations = Vec::new();
|
|
465
468
|
let generation = if let Some(graph) = graph {
|
|
466
469
|
let graph = self.adopt(graph, transition)?;
|
|
467
|
-
|
|
470
|
+
let target = self.agreement(&graph, &transition.target)?;
|
|
471
|
+
if target == Agreement::Exact {
|
|
468
472
|
transition.target.validate_interfaces()?;
|
|
469
473
|
return self.applied(&graph, transition.target.clone());
|
|
470
474
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
475
|
+
// LEGACY(<=4.4.x): the target's own untyped graph is never already
|
|
476
|
+
// applied; it is replaced like a previous graph, in the same batch.
|
|
477
|
+
if target == Agreement::Different {
|
|
478
|
+
let previous = transition.previous.as_ref().ok_or(Error::Conflict)?;
|
|
479
|
+
if !self.matches(&graph, &previous.prepared)? {
|
|
480
|
+
return Err(Error::Conflict);
|
|
481
|
+
}
|
|
474
482
|
}
|
|
475
483
|
for rule in &graph.rules {
|
|
476
484
|
operations.push((
|
|
@@ -533,7 +541,7 @@ impl Owner {
|
|
|
533
541
|
self.binding(&graph, transition.previous.as_ref())?;
|
|
534
542
|
if wire::number(&graph.table, 2)? != 6
|
|
535
543
|
|| wire::number(&graph.table, 7)? != self.socket()?.port
|
|
536
|
-
||
|
|
544
|
+
|| self.agreement(&graph, &transition.target)? != Agreement::Exact
|
|
537
545
|
{
|
|
538
546
|
return Err(Error::Conflict);
|
|
539
547
|
}
|
|
@@ -668,6 +676,61 @@ impl Owner {
|
|
|
668
676
|
}
|
|
669
677
|
}
|
|
670
678
|
|
|
679
|
+
/// How a kernel graph agrees with a compiled one.
|
|
680
|
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
681
|
+
enum Agreement {
|
|
682
|
+
/// Every chain, rule, set and element, with every set's types.
|
|
683
|
+
Exact,
|
|
684
|
+
/// LEGACY(<=4.4.x): the same graph with untyped sets. Remove with the next
|
|
685
|
+
/// major.
|
|
686
|
+
Untyped,
|
|
687
|
+
Different,
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/// Sets by name, each with its identity and sorted element keys.
|
|
691
|
+
type Sets = std::collections::BTreeMap<String, (Vec<u8>, Vec<Vec<u8>>)>;
|
|
692
|
+
/// The compiled graph's sets. LEGACY(<=4.4.x): `untyped` declares them as a
|
|
693
|
+
/// release before typed sets did; remove the parameter with the next major.
|
|
694
|
+
fn expected_sets(program: &[(u16, Vec<Attr>)], untyped: bool) -> Result<Sets> {
|
|
695
|
+
// Element order is the set backend's; element keys are the identity.
|
|
696
|
+
let mut sets = Sets::new();
|
|
697
|
+
for (kind, attrs) in program {
|
|
698
|
+
match kind {
|
|
699
|
+
9 => {
|
|
700
|
+
let identity = if untyped {
|
|
701
|
+
set_identity(&untyped_set(attrs))?
|
|
702
|
+
} else {
|
|
703
|
+
set_identity(attrs)?
|
|
704
|
+
};
|
|
705
|
+
sets.insert(wire::text(attrs, 2)?, (identity, Vec::new()));
|
|
706
|
+
}
|
|
707
|
+
12 => {
|
|
708
|
+
let entry = sets
|
|
709
|
+
.get_mut(&wire::text(attrs, 2)?)
|
|
710
|
+
.ok_or(Error::Invalid)?;
|
|
711
|
+
entry.1.extend(element_keys(&wire::attrs(&wire::one(attrs, 3)?.value)?)?);
|
|
712
|
+
}
|
|
713
|
+
_ => {}
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
for entry in sets.values_mut() {
|
|
717
|
+
entry.1.sort();
|
|
718
|
+
}
|
|
719
|
+
Ok(sets)
|
|
720
|
+
}
|
|
721
|
+
/// LEGACY(<=4.4.x): a set declaration as releases before typed sets wrote it:
|
|
722
|
+
/// key and data type 0 and no userdata. Remove with the next major.
|
|
723
|
+
pub(super) fn untyped_set(attributes: &[Attr]) -> Vec<Attr> {
|
|
724
|
+
attributes
|
|
725
|
+
.iter()
|
|
726
|
+
.filter(|attribute| attribute.id() != 13)
|
|
727
|
+
.map(|attribute| match attribute.id() {
|
|
728
|
+
4 | 6 => Attr::u32(attribute.id(), 0),
|
|
729
|
+
_ => attribute.clone(),
|
|
730
|
+
})
|
|
731
|
+
.collect()
|
|
732
|
+
}
|
|
733
|
+
|
|
671
734
|
fn chain_identity(attributes: &[Attr]) -> Result<Vec<u8>> {
|
|
672
735
|
let mut identity = Vec::new();
|
|
673
736
|
for attribute in attributes {
|
|
@@ -681,9 +744,10 @@ fn chain_identity(attributes: &[Attr]) -> Result<Vec<u8>> {
|
|
|
681
744
|
serde_json::to_vec(&wire::canonical(&identity, false)?).map_err(|_| Error::Protocol)
|
|
682
745
|
}
|
|
683
746
|
/// A set is its table, name, flags, key type and key length, a map's data type
|
|
684
|
-
/// and length,
|
|
685
|
-
///
|
|
686
|
-
///
|
|
747
|
+
/// and length, the field lengths of a concatenation and its userdata (byte
|
|
748
|
+
/// orders and `typeof` descriptions). The handle, the informational backend
|
|
749
|
+
/// type and count, the transaction ID and an empty description are not
|
|
750
|
+
/// identity; anything else rejects.
|
|
687
751
|
fn set_identity(attributes: &[Attr]) -> Result<Vec<u8>> {
|
|
688
752
|
let mut identity = Vec::new();
|
|
689
753
|
for attribute in attributes {
|
|
@@ -694,13 +758,17 @@ fn set_identity(attributes: &[Attr]) -> Result<Vec<u8>> {
|
|
|
694
758
|
identity.push(Attr::bytes(9, fields));
|
|
695
759
|
}
|
|
696
760
|
}
|
|
697
|
-
1..=7 => identity.push(attribute.clone()),
|
|
761
|
+
1..=7 | 13 => identity.push(attribute.clone()),
|
|
698
762
|
_ => return Err(Error::Conflict),
|
|
699
763
|
}
|
|
700
764
|
}
|
|
701
765
|
for id in [1, 2, 3, 4, 5] {
|
|
702
766
|
wire::one(&identity, id)?;
|
|
703
767
|
}
|
|
768
|
+
// At most one userdata: a typed set carries it, a legacy untyped one not.
|
|
769
|
+
if identity.iter().filter(|attribute| attribute.id() == 13).count() > 1 {
|
|
770
|
+
return Err(Error::Conflict);
|
|
771
|
+
}
|
|
704
772
|
// A map carries its data type and length, and nothing else does.
|
|
705
773
|
let map = wire::number(&identity, 3)? & 8 != 0;
|
|
706
774
|
for id in [6, 7] {
|