@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.
- package/changelog.md +7 -0
- package/dist_rust/smartnftables_linux_amd64_musl +0 -0
- package/dist_rust/smartnftables_linux_amd64_musl.tsrust-build.json +4 -4
- package/dist_rust/smartnftables_linux_arm64_musl +0 -0
- package/dist_rust/smartnftables_linux_arm64_musl.tsrust-build.json +4 -4
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/package.json +1 -1
- package/readme.md +52 -1
- package/rust/src/egress.compile.rs +105 -38
- package/rust/src/egress.hostgrant.rs +74 -42
- package/rust/src/egress.keys.rs +380 -55
- package/rust/src/egress.leased.rs +71 -42
- package/rust/src/egress.poolguard.rs +8 -13
- 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 +8 -0
- package/rust/src/egress.workloadgrant.rs +30 -28
- package/rust/src/egress_keys_tests.rs +150 -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/rust/src/egress.keys.rs
CHANGED
|
@@ -1,10 +1,309 @@
|
|
|
1
1
|
//! Concatenated set keys shared by every set-backed part of the compiled graph:
|
|
2
|
-
//! the loads that build
|
|
3
|
-
//! built field by field, the disjoint union the
|
|
4
|
-
//! and set declarations that are never created
|
|
5
|
-
|
|
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.
|
|
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,94 +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
|
-
|
|
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
|
-
|
|
30
|
-
self.
|
|
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(
|
|
330
|
+
self.push(if incoming { IIF } else { OIF })
|
|
331
|
+
}
|
|
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 })
|
|
37
335
|
}
|
|
38
|
-
/// An interface index and name
|
|
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(
|
|
341
|
+
self.push(L4PROTO)
|
|
45
342
|
}
|
|
46
343
|
pub(super) fn protocols(&mut self) -> &mut Self {
|
|
47
|
-
self.protocol().push(
|
|
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(
|
|
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(
|
|
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(
|
|
364
|
+
self.push(if source {
|
|
365
|
+
CT_ORIGINAL_SPORT
|
|
366
|
+
} else {
|
|
367
|
+
CT_ORIGINAL_DPORT
|
|
368
|
+
})
|
|
67
369
|
}
|
|
68
370
|
/// The reply tuple's source address and port: where a translated flow was
|
|
69
371
|
/// translated to, whichever direction the packet travels.
|
|
70
372
|
pub(super) fn translated(&mut self) -> &mut Self {
|
|
71
|
-
self.push(
|
|
72
|
-
.push(|at| ct_reply_to(11, at), PORT)
|
|
373
|
+
self.push(CT_REPLY_SADDR).push(CT_REPLY_SPORT)
|
|
73
374
|
}
|
|
74
375
|
pub(super) fn zone(&mut self) -> &mut Self {
|
|
75
|
-
self.push(
|
|
376
|
+
self.push(CT_ZONE)
|
|
76
377
|
}
|
|
77
378
|
pub(super) fn label(&mut self) -> &mut Self {
|
|
78
|
-
self.push(
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
+
}
|
|
83
404
|
let mut expression = lookup(set);
|
|
84
405
|
if data || invert {
|
|
85
|
-
let mut
|
|
406
|
+
let mut attributes = vec![Attr::string(1, set), Attr::u32(2, register(4))];
|
|
86
407
|
if data {
|
|
87
408
|
// The map's data replaces the key from the first register on.
|
|
88
|
-
|
|
409
|
+
attributes.push(Attr::u32(3, register(4)));
|
|
89
410
|
}
|
|
90
|
-
|
|
91
|
-
expression = expr("lookup",
|
|
411
|
+
attributes.push(Attr::u32(5, u32::from(invert)));
|
|
412
|
+
expression = expr("lookup", attributes);
|
|
92
413
|
}
|
|
93
414
|
result.push(expression);
|
|
94
|
-
result
|
|
415
|
+
Ok(result)
|
|
95
416
|
}
|
|
96
417
|
/// Membership of the loaded key.
|
|
97
|
-
pub(super) fn lookup(&mut self, set: &str) -> Vec<Attr
|
|
98
|
-
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)
|
|
99
420
|
}
|
|
100
421
|
/// A map lookup storing the element's data from the first key register on.
|
|
101
|
-
pub(super) fn map(&mut self, set: &str) -> Vec<Attr
|
|
102
|
-
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)
|
|
103
424
|
}
|
|
104
425
|
/// Absence of the loaded key.
|
|
105
|
-
pub(super) fn absent(&mut self, set: &str) -> Vec<Attr
|
|
106
|
-
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)
|
|
107
428
|
}
|
|
108
429
|
}
|
|
109
430
|
/// Stores `length` bytes of map data from the first key register into a
|
|
@@ -180,13 +501,13 @@ impl Key {
|
|
|
180
501
|
}
|
|
181
502
|
|
|
182
503
|
/// The byte range of each field inside the padded key.
|
|
183
|
-
fn layout(fields: &[
|
|
504
|
+
fn layout(fields: &[Field]) -> Vec<std::ops::Range<usize>> {
|
|
184
505
|
let mut at = 0;
|
|
185
506
|
fields
|
|
186
507
|
.iter()
|
|
187
|
-
.map(|
|
|
188
|
-
let range = at..at +
|
|
189
|
-
at +=
|
|
508
|
+
.map(|field| {
|
|
509
|
+
let range = at..at + field.length as usize;
|
|
510
|
+
at += field.padded() as usize;
|
|
190
511
|
range
|
|
191
512
|
})
|
|
192
513
|
.collect()
|
|
@@ -246,7 +567,7 @@ fn subtract(
|
|
|
246
567
|
/// of a map may overlap only where they carry the same data; anything else is a
|
|
247
568
|
/// policy the compiler cannot express and rejects.
|
|
248
569
|
pub(super) fn disjoint(
|
|
249
|
-
fields: &[
|
|
570
|
+
fields: &[Field],
|
|
250
571
|
mut elements: Vec<IntervalElement>,
|
|
251
572
|
) -> Result<Vec<IntervalElement>> {
|
|
252
573
|
let layout = layout(fields);
|
|
@@ -278,8 +599,8 @@ pub(super) fn interval(
|
|
|
278
599
|
program: &mut Program<'_>,
|
|
279
600
|
name: &str,
|
|
280
601
|
id: u32,
|
|
281
|
-
fields: &[
|
|
282
|
-
data: Option
|
|
602
|
+
fields: &[Field],
|
|
603
|
+
data: Option<&[Field]>,
|
|
283
604
|
elements: Vec<IntervalElement>,
|
|
284
605
|
) -> Result<bool> {
|
|
285
606
|
if elements.is_empty() {
|
|
@@ -295,8 +616,8 @@ pub(super) fn exact(
|
|
|
295
616
|
program: &mut Program<'_>,
|
|
296
617
|
name: &str,
|
|
297
618
|
id: u32,
|
|
298
|
-
|
|
299
|
-
data: Option
|
|
619
|
+
key: &[Field],
|
|
620
|
+
data: Option<&[Field]>,
|
|
300
621
|
mut elements: Vec<(Vec<u8>, Option<Vec<u8>>)>,
|
|
301
622
|
) -> Result<bool> {
|
|
302
623
|
if elements.is_empty() {
|
|
@@ -305,7 +626,11 @@ pub(super) fn exact(
|
|
|
305
626
|
elements.sort();
|
|
306
627
|
elements.dedup();
|
|
307
628
|
require(elements.windows(2).all(|pair| pair[0].0 != pair[1].0))?;
|
|
308
|
-
program.exact_set(name, id,
|
|
629
|
+
program.exact_set(name, id, key, data)?;
|
|
309
630
|
program.exact_elements(name, elements)?;
|
|
310
631
|
Ok(true)
|
|
311
632
|
}
|
|
633
|
+
|
|
634
|
+
#[cfg(test)]
|
|
635
|
+
#[path = "egress_keys_tests.rs"]
|
|
636
|
+
mod tests;
|