@push.rocks/smartnftables 2.6.0 → 4.0.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 +33 -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/classes.manageddockerforwarding.js +3 -4
- package/dist_ts/classes.managednftables.d.ts +14 -2
- package/dist_ts/classes.managednftables.js +48 -8
- package/dist_ts/managed.egress.types.d.ts +25 -3
- package/dist_ts/managed.types.d.ts +14 -0
- package/package.json +7 -7
- package/readme.md +262 -64
- package/rust/src/docker.policy.rs +2 -1
- package/rust/src/egress.compile.rs +153 -76
- package/rust/src/egress.host.rs +24 -51
- package/rust/src/egress.hostgrant.rs +1 -1
- package/rust/src/egress.keys.rs +305 -0
- package/rust/src/egress.leased.rs +399 -0
- package/rust/src/egress.private.rs +197 -0
- package/rust/src/egress.published.rs +370 -0
- package/rust/src/egress.router.rs +61 -294
- package/rust/src/egress.rs +140 -19
- package/rust/src/egress_hostgrant_tests.rs +158 -20
- package/rust/src/egress_localport_tests.rs +11 -2
- package/rust/src/egress_publishedrange_tests.rs +812 -0
- package/rust/src/egress_routerequivalence_tests.rs +448 -0
- package/rust/src/egress_tests.rs +289 -230
- package/rust/src/egress_workloadgrant_tests.rs +21 -13
- package/rust/src/main.rs +57 -3
- package/rust/src/owner.rs +69 -14
- package/rust/src/owner_host_traffic_tests.rs +4 -0
- package/rust/src/owner_identity_tests.rs +166 -80
- package/rust/src/owner_publishedrange_traffic_tests.rs +306 -0
- package/rust/src/owner_router_traffic_tests.rs +2 -0
- package/rust/src/owner_scale_traffic_tests.rs +383 -0
- package/rust/src/owner_tests.rs +6 -0
- package/rust/src/policy.rs +30 -29
- package/rust/src/tests.rs +100 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.manageddockerforwarding.ts +2 -3
- package/ts/classes.managednftables.ts +46 -8
- package/ts/managed.egress.types.ts +25 -3
- package/ts/managed.types.ts +17 -0
|
@@ -6,17 +6,46 @@ use crate::wire::{self, Attr};
|
|
|
6
6
|
mod host;
|
|
7
7
|
#[path = "egress.hostgrant.rs"]
|
|
8
8
|
mod hostgrant;
|
|
9
|
+
#[path = "egress.keys.rs"]
|
|
10
|
+
mod keys;
|
|
11
|
+
#[path = "egress.leased.rs"]
|
|
12
|
+
mod leased;
|
|
9
13
|
#[path = "egress.poolguard.rs"]
|
|
10
14
|
mod poolguard;
|
|
15
|
+
#[path = "egress.private.rs"]
|
|
16
|
+
mod private;
|
|
17
|
+
#[path = "egress.published.rs"]
|
|
18
|
+
mod published;
|
|
11
19
|
#[path = "egress.router.rs"]
|
|
12
20
|
mod router;
|
|
13
21
|
#[path = "egress.workloadgrant.rs"]
|
|
14
22
|
mod workloadgrant;
|
|
15
23
|
|
|
16
|
-
///
|
|
17
|
-
///
|
|
18
|
-
///
|
|
19
|
-
|
|
24
|
+
/// The atomic replacement budget. One replacement is one nfnetlink batch in one
|
|
25
|
+
/// `sendmsg`, and Linux refuses a netlink message above the socket send buffer:
|
|
26
|
+
/// `SO_SNDBUF` clamped to `net.core.wmem_max` and doubled, 425,984 bytes with the
|
|
27
|
+
/// default `wmem_max` of 212,992. That is the hard ceiling; `wire::BATCH_BYTES`
|
|
28
|
+
/// (320,000) is this package's choice, which fits every `wmem_max` from 160,016
|
|
29
|
+
/// up. A batch carries its begin and end messages (at most 64 bytes), the
|
|
30
|
+
/// handle-only deletion of the previous graph — fewer than `OPERATIONS` rules,
|
|
31
|
+
/// chains and sets, each message at most 140 bytes (the longest table name, a
|
|
32
|
+
/// chain name of up to 32 bytes and a handle), 107,520 bytes — and the complete
|
|
33
|
+
/// target: its rules, chains and sets within `RULE_BYTES`, and together with its
|
|
34
|
+
/// set elements within `TARGET_BYTES`. 64 + 107,520 + 212,000 = 319,584 bytes.
|
|
35
|
+
/// The elements take whatever the rules leave: a set replaces a rule per entry,
|
|
36
|
+
/// so the element share grows exactly where the rule share shrinks.
|
|
37
|
+
pub(super) const OPERATIONS: usize = 768;
|
|
38
|
+
pub(super) const RULE_BYTES: usize = 100_000;
|
|
39
|
+
pub(super) const TARGET_BYTES: usize = 212_000;
|
|
40
|
+
|
|
41
|
+
/// One element of an interval set: the first and last key, each field of the
|
|
42
|
+
/// last at or above the same field of the first, and the data of a map.
|
|
43
|
+
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
44
|
+
pub(super) struct IntervalElement {
|
|
45
|
+
pub(super) first: Vec<u8>,
|
|
46
|
+
pub(super) last: Vec<u8>,
|
|
47
|
+
pub(super) data: Option<Vec<u8>>,
|
|
48
|
+
}
|
|
20
49
|
|
|
21
50
|
pub(super) struct Program<'a> {
|
|
22
51
|
table: &'a str,
|
|
@@ -32,10 +61,67 @@ impl<'a> Program<'a> {
|
|
|
32
61
|
// Same atomic replacement reserve as v1: a previous bounded graph must
|
|
33
62
|
// fit its handle-only deletions beside the complete target, and the
|
|
34
63
|
// target's set elements, in one `wire::BATCH_BYTES` batch.
|
|
35
|
-
|
|
64
|
+
crate::capacity("ruleBytes", RULE_BYTES, self.bytes)?;
|
|
65
|
+
self.admit(operation, attributes)
|
|
66
|
+
}
|
|
67
|
+
/// Admits one message within the target and operation budgets.
|
|
68
|
+
fn admit(&mut self, operation: u16, attributes: Vec<Attr>) -> Result<()> {
|
|
69
|
+
crate::capacity("targetBytes", TARGET_BYTES, self.bytes + self.element_bytes)?;
|
|
70
|
+
crate::capacity("operations", OPERATIONS, self.operations.len() + 1)?;
|
|
36
71
|
self.operations.push((operation, attributes));
|
|
37
72
|
Ok(())
|
|
38
73
|
}
|
|
74
|
+
/// Element messages count against the target, beside the rules.
|
|
75
|
+
fn push_elements(&mut self, attributes: Vec<Attr>) -> Result<()> {
|
|
76
|
+
self.element_bytes += wire::encode_attrs(&attributes).len() + 20;
|
|
77
|
+
self.admit(12, attributes)
|
|
78
|
+
}
|
|
79
|
+
/// One named exact-key hash set, or a map to `data_length` bytes. CONSTANT
|
|
80
|
+
/// like every set here.
|
|
81
|
+
fn exact_set(
|
|
82
|
+
&mut self,
|
|
83
|
+
name: &str,
|
|
84
|
+
id: u32,
|
|
85
|
+
key_length: u32,
|
|
86
|
+
data_length: Option<u32>,
|
|
87
|
+
) -> Result<()> {
|
|
88
|
+
let mut attributes = vec![
|
|
89
|
+
Attr::string(1, self.table),
|
|
90
|
+
Attr::string(2, name),
|
|
91
|
+
Attr::u32(3, 2 | if data_length.is_some() { 8 } else { 0 }),
|
|
92
|
+
Attr::u32(4, 0),
|
|
93
|
+
Attr::u32(5, key_length),
|
|
94
|
+
];
|
|
95
|
+
if let Some(length) = data_length {
|
|
96
|
+
attributes.extend([Attr::u32(6, 0), Attr::u32(7, length)]);
|
|
97
|
+
}
|
|
98
|
+
attributes.push(Attr::u32(10, id));
|
|
99
|
+
self.push(9, attributes)
|
|
100
|
+
}
|
|
101
|
+
fn exact_elements(
|
|
102
|
+
&mut self,
|
|
103
|
+
name: &str,
|
|
104
|
+
elements: Vec<(Vec<u8>, Option<Vec<u8>>)>,
|
|
105
|
+
) -> Result<()> {
|
|
106
|
+
for chunk in elements.chunks(256) {
|
|
107
|
+
let list = chunk
|
|
108
|
+
.iter()
|
|
109
|
+
.map(|(key, data)| {
|
|
110
|
+
let mut fields = vec![Attr::nested(1, vec![Attr::bytes(1, key.clone())])];
|
|
111
|
+
if let Some(data) = data {
|
|
112
|
+
fields.push(Attr::nested(2, vec![Attr::bytes(1, data.clone())]));
|
|
113
|
+
}
|
|
114
|
+
Attr::nested(1, fields)
|
|
115
|
+
})
|
|
116
|
+
.collect();
|
|
117
|
+
self.push_elements(vec![
|
|
118
|
+
Attr::string(1, self.table),
|
|
119
|
+
Attr::string(2, name),
|
|
120
|
+
Attr::nested(3, list),
|
|
121
|
+
])?;
|
|
122
|
+
}
|
|
123
|
+
Ok(())
|
|
124
|
+
}
|
|
39
125
|
/// One named exact-match set. CONSTANT: its elements cannot change once a
|
|
40
126
|
/// rule binds it, so replacement always creates a complete new set.
|
|
41
127
|
fn set(&mut self, name: &str, id: u32, key_length: usize) -> Result<()> {
|
|
@@ -56,21 +142,72 @@ impl<'a> Program<'a> {
|
|
|
56
142
|
/// own budget: a replacement deletes the previous set, and so its elements,
|
|
57
143
|
/// by one handle.
|
|
58
144
|
fn elements(&mut self, name: &str, keys: Vec<Vec<u8>>) -> Result<()> {
|
|
59
|
-
|
|
60
|
-
|
|
145
|
+
self.exact_elements(name, keys.into_iter().map(|key| (key, None)).collect())
|
|
146
|
+
}
|
|
147
|
+
/// One named CONSTANT set of concatenated fields in which every field is an
|
|
148
|
+
/// inclusive interval (the kernel's pipapo backend), optionally a map to
|
|
149
|
+
/// `data_length` bytes. `fields` are byte lengths; each occupies whole
|
|
150
|
+
/// 32-bit registers of the key.
|
|
151
|
+
fn interval_set(
|
|
152
|
+
&mut self,
|
|
153
|
+
name: &str,
|
|
154
|
+
id: u32,
|
|
155
|
+
fields: &[u32],
|
|
156
|
+
data_length: Option<u32>,
|
|
157
|
+
) -> Result<()> {
|
|
158
|
+
let key_length: u32 = fields.iter().map(|length| length.div_ceil(4) * 4).sum();
|
|
159
|
+
let mut attributes = vec![
|
|
160
|
+
Attr::string(1, self.table),
|
|
161
|
+
Attr::string(2, name),
|
|
162
|
+
// CONSTANT | INTERVAL | CONCAT, and MAP for a map.
|
|
163
|
+
Attr::u32(3, 2 | 4 | 0x80 | if data_length.is_some() { 8 } else { 0 }),
|
|
164
|
+
Attr::u32(4, 0),
|
|
165
|
+
Attr::u32(5, key_length),
|
|
166
|
+
];
|
|
167
|
+
if let Some(length) = data_length {
|
|
168
|
+
attributes.extend([Attr::u32(6, 0), Attr::u32(7, length)]);
|
|
169
|
+
}
|
|
170
|
+
attributes.extend([
|
|
171
|
+
Attr::nested(
|
|
172
|
+
9,
|
|
173
|
+
vec![Attr::nested(
|
|
174
|
+
2,
|
|
175
|
+
fields
|
|
176
|
+
.iter()
|
|
177
|
+
.map(|length| Attr::nested(1, vec![Attr::u32(1, *length)]))
|
|
178
|
+
.collect(),
|
|
179
|
+
)],
|
|
180
|
+
),
|
|
181
|
+
Attr::u32(10, id),
|
|
182
|
+
]);
|
|
183
|
+
self.push(9, attributes)
|
|
184
|
+
}
|
|
185
|
+
/// Interval elements `(first, last, data)`, 256 to a message like `elements`.
|
|
186
|
+
/// An element whose bounds are equal carries no last key; Linux takes the
|
|
187
|
+
/// first as the last and dumps it the same way.
|
|
188
|
+
fn interval_elements(&mut self, name: &str, elements: Vec<IntervalElement>) -> Result<()> {
|
|
189
|
+
for chunk in elements.chunks(256) {
|
|
190
|
+
let list = chunk
|
|
61
191
|
.iter()
|
|
62
|
-
.map(|
|
|
63
|
-
|
|
192
|
+
.map(|element| {
|
|
193
|
+
let value = |kind: u16, bytes: &[u8]| {
|
|
194
|
+
Attr::nested(kind, vec![Attr::bytes(1, bytes.to_vec())])
|
|
195
|
+
};
|
|
196
|
+
let mut fields = vec![value(1, &element.first)];
|
|
197
|
+
if element.last != element.first {
|
|
198
|
+
fields.push(value(10, &element.last));
|
|
199
|
+
}
|
|
200
|
+
if let Some(data) = &element.data {
|
|
201
|
+
fields.push(value(2, data));
|
|
202
|
+
}
|
|
203
|
+
Attr::nested(1, fields)
|
|
64
204
|
})
|
|
65
205
|
.collect();
|
|
66
|
-
|
|
206
|
+
self.push_elements(vec![
|
|
67
207
|
Attr::string(1, self.table),
|
|
68
208
|
Attr::string(2, name),
|
|
69
|
-
Attr::nested(3,
|
|
70
|
-
]
|
|
71
|
-
self.element_bytes += wire::encode_attrs(&attributes).len() + 20;
|
|
72
|
-
require(self.element_bytes <= ELEMENT_BYTES && self.operations.len() < 768)?;
|
|
73
|
-
self.operations.push((12, attributes));
|
|
209
|
+
Attr::nested(3, list),
|
|
210
|
+
])?;
|
|
74
211
|
}
|
|
75
212
|
Ok(())
|
|
76
213
|
}
|
|
@@ -243,12 +380,6 @@ fn immediate(register: u32, value: Vec<u8>) -> Attr {
|
|
|
243
380
|
],
|
|
244
381
|
)
|
|
245
382
|
}
|
|
246
|
-
fn set_ct(key: u32, value: Vec<u8>) -> Vec<Attr> {
|
|
247
|
-
vec![
|
|
248
|
-
immediate(1, value),
|
|
249
|
-
expr("ct", vec![Attr::u32(2, key), Attr::u32(4, 1)]),
|
|
250
|
-
]
|
|
251
|
-
}
|
|
252
383
|
fn label(generation: &Generation) -> Vec<u8> {
|
|
253
384
|
// A label is an opaque canonical 16-byte value, never a host-endian integer.
|
|
254
385
|
generation
|
|
@@ -258,14 +389,6 @@ fn label(generation: &Generation) -> Vec<u8> {
|
|
|
258
389
|
.map(|pair| u8::from_str_radix(std::str::from_utf8(pair).unwrap(), 16).unwrap())
|
|
259
390
|
.collect()
|
|
260
391
|
}
|
|
261
|
-
fn generation(generation: &Generation, direction: u8, labelled: bool) -> Vec<Attr> {
|
|
262
|
-
let mut result = ct(17, None, generation.conntrack_zone.to_ne_bytes().to_vec());
|
|
263
|
-
result.extend(ct(1, None, vec![direction]));
|
|
264
|
-
if labelled {
|
|
265
|
-
result.extend(ct(13, None, label(generation)));
|
|
266
|
-
}
|
|
267
|
-
result
|
|
268
|
-
}
|
|
269
392
|
fn state(value: u32) -> Vec<Attr> {
|
|
270
393
|
ct(0, None, value.to_ne_bytes().to_vec())
|
|
271
394
|
}
|
|
@@ -297,50 +420,6 @@ fn snat(address: &str, range: Option<(u16, u16)>) -> Result<Vec<Attr>> {
|
|
|
297
420
|
result.push(expr("nat", data));
|
|
298
421
|
Ok(result)
|
|
299
422
|
}
|
|
300
|
-
/// Published inbound translation. Address and port are always both specified, so
|
|
301
|
-
/// the kernel never picks a port and the graph identity carries explicit registers.
|
|
302
|
-
fn dnat(address: &str, port: u16) -> Result<Vec<Attr>> {
|
|
303
|
-
Ok(vec![
|
|
304
|
-
immediate(1, super::address(address)?.to_be_bytes().to_vec()),
|
|
305
|
-
immediate(2, port.to_be_bytes().to_vec()),
|
|
306
|
-
expr(
|
|
307
|
-
"nat",
|
|
308
|
-
vec![
|
|
309
|
-
Attr::u32(1, 1),
|
|
310
|
-
Attr::u32(2, 2),
|
|
311
|
-
Attr::u32(3, 1),
|
|
312
|
-
Attr::u32(4, 1),
|
|
313
|
-
Attr::u32(5, 2),
|
|
314
|
-
Attr::u32(6, 2),
|
|
315
|
-
Attr::u32(7, 3),
|
|
316
|
-
],
|
|
317
|
-
),
|
|
318
|
-
])
|
|
319
|
-
}
|
|
320
|
-
fn tuple(grant: &Grant, protection: &Protection, reply: bool, original: bool) -> Result<Vec<Attr>> {
|
|
321
|
-
let destination = super::destination(grant, protection)?;
|
|
322
|
-
let mut result = ipv4();
|
|
323
|
-
result.extend(meta(16, vec![protocol(&grant.protocol)]));
|
|
324
|
-
result.extend(current_address(&grant.source_prefix, !reply)?);
|
|
325
|
-
result.extend(current_address(&destination, reply)?);
|
|
326
|
-
for (port, source) in [(grant.source_port, !reply), (grant.destination_port, reply)] {
|
|
327
|
-
if let Some(port) = port {
|
|
328
|
-
result.extend(current_port(port, source));
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
if original {
|
|
332
|
-
result.extend(original_address(&grant.source_prefix, true)?);
|
|
333
|
-
result.extend(original_address(&destination, false)?);
|
|
334
|
-
result.extend(ct(10, None, vec![protocol(&grant.protocol)]));
|
|
335
|
-
for (port, source) in [(grant.source_port, true), (grant.destination_port, false)] {
|
|
336
|
-
if let Some(port) = port {
|
|
337
|
-
result.extend(original_port(port, source));
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
Ok(result)
|
|
342
|
-
}
|
|
343
|
-
|
|
344
423
|
pub(super) fn compile(prepared: &Prepared, table: &str) -> Result<Vec<(u16, Vec<Attr>)>> {
|
|
345
424
|
let mut program = Program {
|
|
346
425
|
table,
|
|
@@ -351,9 +430,7 @@ pub(super) fn compile(prepared: &Prepared, table: &str) -> Result<Vec<(u16, Vec<
|
|
|
351
430
|
operations: Vec::new(),
|
|
352
431
|
};
|
|
353
432
|
match &prepared.policy.scope {
|
|
354
|
-
Scope::RouterEgress(scope) =>
|
|
355
|
-
router::compile(&mut program, scope, prepared.policy.revision)?
|
|
356
|
-
}
|
|
433
|
+
Scope::RouterEgress(scope) => router::compile(&mut program, scope)?,
|
|
357
434
|
Scope::HostTransit(scope) => host::compile(&mut program, scope)?,
|
|
358
435
|
Scope::AllocationPoolGuard(scope) => poolguard::compile(&mut program, scope)?,
|
|
359
436
|
}
|
package/rust/src/egress.host.rs
CHANGED
|
@@ -66,34 +66,12 @@ fn leased(
|
|
|
66
66
|
expressions.extend(original_range(range.first, range.last));
|
|
67
67
|
Ok(expressions)
|
|
68
68
|
}
|
|
69
|
-
///
|
|
70
|
-
///
|
|
71
|
-
///
|
|
72
|
-
fn
|
|
73
|
-
|
|
74
|
-
handoff: &HostHandoff,
|
|
75
|
-
port: &PublishedPort,
|
|
76
|
-
reply: bool,
|
|
77
|
-
) -> Result<Vec<Attr>> {
|
|
78
|
-
let mut expressions = ipv4();
|
|
79
|
-
expressions.extend(link(&scope.uplink, !reply));
|
|
80
|
-
expressions.extend(link(&handoff.link, reply));
|
|
81
|
-
expressions.extend(meta(16, vec![protocol(&port.protocol)]));
|
|
82
|
-
expressions.extend(ct(10, None, vec![protocol(&port.protocol)]));
|
|
83
|
-
expressions.extend(ct(17, None, 0_u16.to_ne_bytes().to_vec()));
|
|
84
|
-
expressions.extend(ct(1, None, vec![u8::from(reply)]));
|
|
85
|
-
let target = format!("{}/32", port.target_address);
|
|
86
|
-
expressions.extend(current_address(&target, reply)?);
|
|
87
|
-
expressions.extend(current_port(port.target_port, reply));
|
|
88
|
-
let published = format!("{}/32", port.host_ip);
|
|
89
|
-
expressions.extend(original_address(&published, false)?);
|
|
90
|
-
expressions.extend(original_port(port.host_port, false));
|
|
91
|
-
Ok(expressions)
|
|
92
|
-
}
|
|
93
|
-
fn published(program: &mut Program<'_>, scope: &HostScope) -> Result<()> {
|
|
69
|
+
/// The publications of this hop. Validation admits only leased transit source
|
|
70
|
+
/// addresses as targets, and one address never spans two active handoff links,
|
|
71
|
+
/// so each publication has exactly one handoff.
|
|
72
|
+
fn hop(scope: &HostScope) -> Result<published::Hop<'_>> {
|
|
73
|
+
let mut publications = Vec::new();
|
|
94
74
|
for port in &scope.published_ports {
|
|
95
|
-
// Validation admits only leased transit source addresses, and one address
|
|
96
|
-
// never spans two active handoff links.
|
|
97
75
|
let handoff = scope
|
|
98
76
|
.handoffs
|
|
99
77
|
.iter()
|
|
@@ -104,30 +82,18 @@ fn published(program: &mut Program<'_>, scope: &HostScope) -> Result<()> {
|
|
|
104
82
|
.any(|allocation| allocation.transit_source_address == port.target_address)
|
|
105
83
|
})
|
|
106
84
|
.ok_or(crate::Error::Invalid)?;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
program.end("forward", expressions, 1)?;
|
|
115
|
-
}
|
|
116
|
-
let mut reply = published_envelope(scope, handoff, port, true)?;
|
|
117
|
-
reply.extend(state(2));
|
|
118
|
-
program.end("forward", reply, 1)?;
|
|
119
|
-
let mut translation = ipv4();
|
|
120
|
-
translation.extend(link(&scope.uplink, true));
|
|
121
|
-
translation.extend(meta(16, vec![protocol(&port.protocol)]));
|
|
122
|
-
translation.extend(ct(10, None, vec![protocol(&port.protocol)]));
|
|
123
|
-
translation.extend(ct(17, None, 0_u16.to_ne_bytes().to_vec()));
|
|
124
|
-
translation.extend(ct(1, None, vec![0]));
|
|
125
|
-
translation.extend(current_address(&format!("{}/32", port.host_ip), false)?);
|
|
126
|
-
translation.extend(current_port(port.host_port, false));
|
|
127
|
-
translation.extend(dnat(&port.target_address, port.target_port)?);
|
|
128
|
-
program.rule("pre", translation)?;
|
|
85
|
+
publications.push(published::Publication {
|
|
86
|
+
protocol: &port.protocol,
|
|
87
|
+
outside: (&port.host_ip, port.host_port, port.host_last()),
|
|
88
|
+
inside: (&port.target_address, port.target_port, port.target_last()),
|
|
89
|
+
link: &handoff.link,
|
|
90
|
+
symmetric: port.symmetric,
|
|
91
|
+
});
|
|
129
92
|
}
|
|
130
|
-
Ok(
|
|
93
|
+
Ok(published::Hop {
|
|
94
|
+
publications,
|
|
95
|
+
fixed: &scope.uplink,
|
|
96
|
+
})
|
|
131
97
|
}
|
|
132
98
|
/// The exact platform endpoints served on host addresses. Every packet is
|
|
133
99
|
/// checked against the live and the originally tracked destination tuple, so a
|
|
@@ -235,12 +201,15 @@ pub(super) fn compile(program: &mut Program<'_>, scope: &HostScope) -> Result<()
|
|
|
235
201
|
.ok_or(crate::Error::Invalid)
|
|
236
202
|
})?;
|
|
237
203
|
}
|
|
204
|
+
let hop = hop(scope)?;
|
|
205
|
+
published::sets(program, &hop, false)?;
|
|
238
206
|
protection(program, scope, false)?;
|
|
239
207
|
protection(program, scope, true)?;
|
|
208
|
+
published::translations(program, &hop)?;
|
|
240
209
|
// Published admission precedes the capture barrier: the barrier denies every
|
|
241
210
|
// protected original destination, which includes this uplink address, and an
|
|
242
211
|
// exact inbound publication is the explicit authority for that exception.
|
|
243
|
-
published(program,
|
|
212
|
+
published::inbound(program, &hop)?;
|
|
244
213
|
// Host-origin grants leave through the exact handoff that holds their source
|
|
245
214
|
// address, which is part of every set key, and precede the terminal output
|
|
246
215
|
// and input denials. Nothing else of the host's own traffic crosses a handoff.
|
|
@@ -275,6 +244,10 @@ pub(super) fn compile(program: &mut Program<'_>, scope: &HostScope) -> Result<()
|
|
|
275
244
|
}
|
|
276
245
|
}
|
|
277
246
|
}
|
|
247
|
+
// Symmetric flows leave from ports outside every leased range of their
|
|
248
|
+
// address, after the capture barrier and before the terminal handoff denials.
|
|
249
|
+
published::symmetric(program, &hop)?;
|
|
250
|
+
published::symmetric_translation(program, &hop)?;
|
|
278
251
|
for handoff in &scope.handoffs {
|
|
279
252
|
program.deny_link("input", &handoff.link, true)?;
|
|
280
253
|
program.deny_link("output", &handoff.link, false)?;
|
|
@@ -38,7 +38,7 @@ pub(super) fn payload_to(base: u32, offset: u32, length: u32, index: u32) -> Att
|
|
|
38
38
|
index,
|
|
39
39
|
)
|
|
40
40
|
}
|
|
41
|
-
fn ct_to(key: u32, original: bool, index: u32) -> Attr {
|
|
41
|
+
pub(super) fn ct_to(key: u32, original: bool, index: u32) -> Attr {
|
|
42
42
|
let mut data = vec![Attr::u32(2, key)];
|
|
43
43
|
if original {
|
|
44
44
|
data.push(Attr::bytes(3, vec![0]));
|