@push.rocks/smartnftables 3.0.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 +14 -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/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.types.d.ts +14 -0
- package/package.json +2 -2
- package/readme.md +58 -2
- package/rust/src/docker.policy.rs +2 -1
- package/rust/src/egress.compile.rs +8 -10
- package/rust/src/egress.rs +18 -12
- package/rust/src/egress_hostgrant_tests.rs +9 -2
- package/rust/src/egress_localport_tests.rs +8 -1
- package/rust/src/egress_tests.rs +157 -0
- package/rust/src/egress_workloadgrant_tests.rs +15 -6
- package/rust/src/main.rs +57 -3
- package/rust/src/policy.rs +10 -13
- 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.types.ts +17 -0
package/changelog.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026-09-28 - 4.0.0
|
|
4
|
+
|
|
5
|
+
### Breaking Changes
|
|
6
|
+
|
|
7
|
+
- Distinguish capacity exhaustion from invalid input: a well-formed policy that does not fit one atomic replacement is refused with the new `ManagedNftablesError` code `EXHAUSTED` instead of `INVALID`. That covers the compiled target budget (`ruleBytes` 100,000, `targetBytes` 212,000, `operations` 768) and the input counts that exist to hold it (schema-v1 32 endpoints and 128 rules; router 128 endpoints and links, 1024 rules and 1024 egress grants; 1024 host grants, workload grants and publications per scope; 8 loopback TCP port owners), and the Docker forwarding contribution's 192 rules and 100,000 command bytes. Bounds of the topology's shape (identifier and interface-name lengths, protected prefixes, platform endpoints, generations, handoffs, allocations, ranges, guarded pools, IPC and capture limits) stay `INVALID`. The accept/reject set, digests and compiled bytes are unchanged. Consumers that treat `INVALID` from `prepare()` as "too big, retry smaller" must switch to `EXHAUSTED`, which is now the only code for that case. The message of an `INVALID` or `EXHAUSTED` error now ends in `: <reason>` (for example `Managed nftables INVALID: Managed policy input is invalid.`), so a consumer matching on the message sees a different text.
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- Carry the native refusal on `ManagedNftablesError` for `INVALID` and `EXHAUSTED`: `reason` holds the bounded native text (also appended to the message), and `EXHAUSTED` names its bound in `details` as `{ bound, limit, actual }` (`TManagedNftCapacityBound`, `IManagedNftCapacity`). A native refusal outside that shape is `PROTOCOL`. Other codes and the facade's local rejections keep their previous shape and message. Like `INVALID`, `EXHAUSTED` is effect-free and does not abort `failureSignal`.
|
|
12
|
+
|
|
13
|
+
### Maintenance
|
|
14
|
+
|
|
15
|
+
- Update `@push.rocks/smartrust` from 2.3.0 to 2.5.0, keeping the exact pin, for `RustBridgeRequestError.responseErrorData` (2.4.0), which carries the refusal's bound. The 2.5.0 cancellation protocol is opt-in and the native owner does not announce it, so requests behave as before.
|
|
16
|
+
|
|
3
17
|
## 2026-09-28 - 3.0.0
|
|
4
18
|
|
|
5
19
|
### Breaking Changes
|
|
Binary file
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "tsrust.build-provenance.v2",
|
|
3
|
-
"binarySha256": "
|
|
3
|
+
"binarySha256": "e879efc88997e38bb45c045fae690c3a07a3ff515c2a2581689466e3088ca236",
|
|
4
4
|
"buildInfo": {
|
|
5
5
|
"projectName": "@push.rocks/smartnftables",
|
|
6
|
-
"projectVersion": "
|
|
7
|
-
"gitCommit": "
|
|
6
|
+
"projectVersion": "4.0.0",
|
|
7
|
+
"gitCommit": "7c66795f5e0edea87f2c9f7f1f70455cdf7e6bab",
|
|
8
8
|
"gitDirty": false,
|
|
9
|
-
"builtAt": "2026-09-
|
|
9
|
+
"builtAt": "2026-09-28T03:45:45.694Z",
|
|
10
10
|
"tsrustVersion": "1.15.1",
|
|
11
11
|
"binary": "smartnftables",
|
|
12
12
|
"target": "linux_amd64_musl"
|
|
Binary file
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"format": "tsrust.build-provenance.v2",
|
|
3
|
-
"binarySha256": "
|
|
3
|
+
"binarySha256": "268d3371a7986e7f1efcd6fb7fdd02b04635060a3e845c0ef45baec75e16a32a",
|
|
4
4
|
"buildInfo": {
|
|
5
5
|
"projectName": "@push.rocks/smartnftables",
|
|
6
|
-
"projectVersion": "
|
|
7
|
-
"gitCommit": "
|
|
6
|
+
"projectVersion": "4.0.0",
|
|
7
|
+
"gitCommit": "7c66795f5e0edea87f2c9f7f1f70455cdf7e6bab",
|
|
8
8
|
"gitDirty": false,
|
|
9
|
-
"builtAt": "2026-09-
|
|
9
|
+
"builtAt": "2026-09-28T03:45:55.540Z",
|
|
10
10
|
"tsrustVersion": "1.15.1",
|
|
11
11
|
"binary": "smartnftables",
|
|
12
12
|
"target": "linux_arm64_musl"
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export const commitinfo = {
|
|
5
5
|
name: '@push.rocks/smartnftables',
|
|
6
|
-
version: '
|
|
6
|
+
version: '4.0.0',
|
|
7
7
|
description: 'A TypeScript module for managing nftables rules including NAT, firewall, and rate limiting with a high-level API.'
|
|
8
8
|
};
|
|
9
9
|
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSwyQkFBMkI7SUFDakMsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLG1IQUFtSDtDQUNqSSxDQUFBIn0=
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
|
-
import { snapshot, managedIpcBytes, ManagedNftablesError } from './classes.managednftables.js';
|
|
2
|
+
import { snapshot, managedIpcBytes, ManagedNftablesError, nativeError } from './classes.managednftables.js';
|
|
3
3
|
/** One node-level Docker DOCKER-USER contribution. Caller owns durable intents and handoff lifetimes. */
|
|
4
4
|
export class ManagedDockerForwarding {
|
|
5
5
|
#options;
|
|
@@ -117,12 +117,11 @@ export class ManagedDockerForwarding {
|
|
|
117
117
|
const owned = work.catch((error) => {
|
|
118
118
|
if (this.#state !== 'closing')
|
|
119
119
|
this.#state = 'failed-owned';
|
|
120
|
-
|
|
121
|
-
throw new ManagedNftablesError(typeof code === 'string' && /^[A-Z_]{1,64}$/.test(code) ? code : 'UNAVAILABLE');
|
|
120
|
+
throw nativeError(error);
|
|
122
121
|
});
|
|
123
122
|
this.#work = owned;
|
|
124
123
|
void owned.then(() => { this.#work = null; }, () => { this.#work = null; });
|
|
125
124
|
return owned;
|
|
126
125
|
}
|
|
127
126
|
}
|
|
128
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
127
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5tYW5hZ2VkZG9ja2VyZm9yd2FyZGluZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXNzZXMubWFuYWdlZGRvY2tlcmZvcndhcmRpbmcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFDeEMsT0FBTyxFQUFFLFFBQVEsRUFBRSxlQUFlLEVBQUUsb0JBQW9CLEVBQUUsV0FBVyxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFHNUcseUdBQXlHO0FBQ3pHLE1BQU0sT0FBTyx1QkFBdUI7SUFDekIsUUFBUSxDQUE0QztJQUM3RCxPQUFPLEdBQTBFLElBQUksQ0FBQztJQUN0RixNQUFNLEdBQTBFLEtBQUssQ0FBQztJQUN0RixLQUFLLEdBQTRCLElBQUksQ0FBQztJQUN0QyxNQUFNLEdBQXlCLElBQUksQ0FBQztJQUNwQyxhQUFhLEdBQUcsS0FBSyxDQUFDO0lBRXRCLFlBQVksT0FBd0M7UUFDbEQsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUMsT0FBTyxDQUFDLENBQUM7UUFDbEMsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxvQkFBb0IsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQztlQUNySCxDQUFDLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxPQUFPLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSx1QkFBdUIsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7ZUFDckksSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsTUFBTSxHQUFHLEVBQUU7ZUFDcEMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLEtBQUssU0FBUyxJQUFJLENBQUMsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDLFVBQVUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsQ0FBQyxFQUFFLENBQUM7WUFDL0ksTUFBTSxJQUFJLG9CQUFvQixDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzVDLENBQUM7UUFDRCxJQUFJLElBQUksQ0FBQyxRQUFRLENBQUMsa0JBQWtCLEtBQUssU0FBUyxJQUFJLENBQUMsT0FBTyxDQUFDLFFBQVEsS0FBSyxPQUFPO2VBQzlFLENBQUMsTUFBTSxDQUFDLFNBQVMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLGtCQUFrQixDQUFDLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsR0FBRyxDQUFDO2VBQzNGLElBQUksQ0FBQyxRQUFRLENBQUMsa0JBQWtCLEdBQUcsV0FBVyxDQUFDO1lBQUUsTUFBTSxJQUFJLG9CQUFvQixDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ2xHLENBQUM7SUFFRCxJQUFXLEtBQUssS0FBYSxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO0lBRWxELHVGQUF1RjtJQUNoRixLQUFLO1FBQ1YsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLEtBQUs7WUFBRSxPQUFPLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxvQkFBb0IsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDO1FBQzFGLElBQUksQ0FBQyxNQUFNLEdBQUcsVUFBVSxDQUFDO1FBQ3pCLE1BQU0sSUFBSSxHQUFHLENBQUMsS0FBSyxJQUFJLEVBQUU7WUFDdkIsSUFBSSxPQUFPLENBQUMsUUFBUSxLQUFLLE9BQU8sSUFBSSxDQUFDLENBQUMsS0FBSyxFQUFFLE9BQU8sQ0FBQyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsSUFBSSxDQUFDO2dCQUFFLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDO1lBQ3JJLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLElBQUksR0FBRyxDQUFDLEtBQUssRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUM7WUFDeEUsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLE9BQU8sQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFtQztnQkFDaEYsVUFBVSxFQUFFLGVBQWUsRUFBRSxVQUFVLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVO3VCQUM1RCxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsV0FBVyxFQUFFLHVCQUF1QixPQUFPLENBQUMsSUFBSSxLQUFLLEtBQUssQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxPQUFPLE9BQU8sQ0FBQztnQkFDbkgsVUFBVSxFQUFFLEVBQUUsRUFBRSxnQkFBZ0IsRUFBRSxLQUFLO2dCQUN2QyxPQUFPLEVBQUUsQ0FBQyxxQkFBcUIsRUFBRSxHQUFHLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyx3QkFBd0IsRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUM1SCx3QkFBd0IsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLGtCQUFrQixLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsa0JBQWtCLENBQUM7Z0JBQ3pILGNBQWMsRUFBRSxJQUFJLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxFQUFFLGNBQWMsRUFBRSxlQUFlO2dCQUMvRSwwQkFBMEIsRUFBRSxFQUFFLHVCQUF1QixFQUFFLENBQUMsRUFBRSxvQkFBb0IsRUFBRSxDQUFDLEVBQUUseUJBQXlCLEVBQUUsQ0FBQyxFQUFFLHVCQUF1QixFQUFFLENBQUMsRUFBRSx1QkFBdUIsRUFBRSxDQUFDLEVBQUUscUJBQXFCLEVBQUUsQ0FBQyxFQUFFO2FBQ3BNLENBQUMsQ0FBQztZQUNILElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUU7Z0JBQzNCLElBQUksQ0FBQyxDQUFDLFNBQVMsRUFBRSxTQUFTLENBQUMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztvQkFBRSxJQUFJLENBQUMsTUFBTSxHQUFHLGNBQWMsQ0FBQztZQUNsRixDQUFDLENBQUMsQ0FBQztZQUNILElBQUksQ0FBQyxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsS0FBSyxFQUFFO2dCQUFFLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxhQUFhLENBQUMsQ0FBQztZQUMvRSxNQUFNLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUM7WUFDOUMsTUFBTSxNQUFNLEdBQUcsUUFBUSxDQUFDLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsc0JBQXNCLEVBQUUsRUFBRSxPQUFPLEVBQUUsVUFBVSxFQUFFLENBQUMsQ0FBQyxDQUFDO1lBQ3pHLElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxVQUFVO2dCQUFFLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxhQUFhLENBQUMsQ0FBQztZQUM5RSxJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQztZQUFDLE9BQU8sTUFBTSxDQUFDO1FBQ3ZDLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDTCxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDM0IsQ0FBQztJQUVNLE9BQU8sQ0FBQyxNQUFzQztRQUNuRCxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMseUJBQXlCLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDekQsQ0FBQztJQUNNLFNBQVMsQ0FBQyxVQUE4QztRQUM3RCxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsMkJBQTJCLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFDL0QsQ0FBQztJQUNNLE9BQU8sS0FBOEMsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLHlCQUF5QixFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUMxRyxPQUFPLENBQUMsUUFBK0M7UUFDNUQsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLHlCQUF5QixFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQzNELENBQUM7SUFFRCxzSUFBc0k7SUFDL0gsS0FBSztRQUNWLElBQUksSUFBSSxDQUFDLE1BQU07WUFBRSxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUM7UUFDcEMsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLFNBQVM7WUFBRSxPQUFPLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUN4RCxJQUFJLENBQUMsTUFBTSxHQUFHLFNBQVMsQ0FBQztRQUN4QixJQUFJLENBQUMsTUFBTSxHQUFHLENBQUMsS0FBSyxJQUFJLEVBQUU7WUFDeEIsTUFBTSxJQUFJLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxHQUFHLEVBQUUsQ0FBQyxTQUFTLENBQUMsQ0FBQztZQUN6QyxJQUFJLElBQUksQ0FBQyxPQUFPLEVBQUUsQ0FBQztnQkFDakIsSUFBSSxJQUFJLENBQUMsYUFBYSxFQUFFLENBQUM7b0JBQ3ZCLE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsdUJBQXVCLEVBQUUsRUFBRSxDQUFDLENBQUM7b0JBQzNFLElBQUksTUFBTSxDQUFDLFFBQVEsS0FBSyxJQUFJO3dCQUFFLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxVQUFVLENBQUMsQ0FBQztnQkFDM0UsQ0FBQztnQkFDRCxNQUFNLElBQUksQ0FBQyxPQUFPLENBQUMsU0FBUyxDQUFDLEVBQUUsYUFBYSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7WUFDeEQsQ0FBQztZQUNELElBQUksQ0FBQyxPQUFPLEdBQUcsSUFBSSxDQUFDO1lBQUMsSUFBSSxDQUFDLE1BQU0sR0FBRyxTQUFTLENBQUM7UUFDL0MsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFO1lBQ2QsSUFBSSxDQUFDLE1BQU0sR0FBRyxjQUFjLENBQUM7WUFBQyxJQUFJLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQztZQUNqRCxNQUFNLElBQUksb0JBQW9CLENBQUMscUJBQXFCLENBQUMsQ0FBQztRQUN4RCxDQUFDLENBQUMsQ0FBQztRQUNILE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUNyQixDQUFDO0lBRUQsT0FBTyxDQUFtRCxNQUFTLEVBQUUsTUFBcUQ7UUFDeEgsSUFBSSxDQUFDLENBQUMsT0FBTyxFQUFFLGNBQWMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxJQUFJLElBQUksQ0FBQyxLQUFLO1lBQUUsT0FBTyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksb0JBQW9CLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQztRQUNwSixJQUFJLFFBQXVELENBQUM7UUFDNUQsSUFBSSxDQUFDO1lBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQyxNQUFNLENBQUMsQ0FBQztRQUFDLENBQUM7UUFBQyxNQUFNLENBQUM7WUFBQyxPQUFPLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxvQkFBb0IsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO1FBQUMsQ0FBQztRQUMxRyxJQUFJLE1BQU0sS0FBSywyQkFBMkIsSUFBSSxNQUFNLEtBQUsseUJBQXlCO1lBQUUsSUFBSSxDQUFDLGFBQWEsR0FBRyxJQUFJLENBQUM7UUFDOUcsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLE1BQU0sRUFBRSxRQUFRLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLEVBQUUsRUFBRTtZQUM1RSxNQUFNLGNBQWMsR0FBRyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7WUFDeEMsSUFBSSxNQUFNLEtBQUsseUJBQXlCO2dCQUFFLElBQUksQ0FBQyxhQUFhLEdBQUcsS0FBSyxDQUFDO1lBQ3JFLElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxTQUFTLElBQUksQ0FBQyxNQUFNLEtBQUssMkJBQTJCLElBQUksTUFBTSxLQUFLLHlCQUF5QjttQkFDM0csTUFBTSxLQUFLLHlCQUF5QixJQUFLLGNBQWlELENBQUMsS0FBSyxLQUFLLElBQUksQ0FBQztnQkFBRSxJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQztZQUN2SSxPQUFPLGNBQWMsQ0FBQztRQUN4QixDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ04sQ0FBQztJQUNELE1BQU0sQ0FBSSxJQUFnQjtRQUN4QixNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUU7WUFDakMsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLFNBQVM7Z0JBQUUsSUFBSSxDQUFDLE1BQU0sR0FBRyxjQUFjLENBQUM7WUFDNUQsTUFBTSxXQUFXLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDM0IsQ0FBQyxDQUFDLENBQUM7UUFDSCxJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQztRQUNuQixLQUFLLEtBQUssQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLEVBQUUsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM1RSxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUM7Q0FDRiJ9
|
|
@@ -1,8 +1,20 @@
|
|
|
1
|
-
import type { IManagedNftOptions, IManagedNftPolicy, IManagedNftTransition, IAppliedManagedNftPolicy, IPreparedManagedNftPolicy, IManagedNftStatus, IManagedNftTransitionRelease, TManagedNftPolicy } from './managed.types.js';
|
|
1
|
+
import type { IManagedNftOptions, IManagedNftPolicy, IManagedNftTransition, IAppliedManagedNftPolicy, IPreparedManagedNftPolicy, IManagedNftStatus, IManagedNftTransitionRelease, TManagedNftPolicy, IManagedNftCapacity } from './managed.types.js';
|
|
2
|
+
/** `INVALID`: the input is outside the contract. `EXHAUSTED`: the policy exceeds a
|
|
3
|
+
* capacity bound of the atomic replacement budget, and a smaller policy may fit.
|
|
4
|
+
* Both carry the native refusal text in `reason`; `EXHAUSTED` also names its
|
|
5
|
+
* bound in `details`. Every other code, and the facade's own rejections, carry
|
|
6
|
+
* neither. */
|
|
2
7
|
export declare class ManagedNftablesError extends Error {
|
|
3
8
|
readonly code: string;
|
|
4
|
-
|
|
9
|
+
readonly reason: string | null;
|
|
10
|
+
readonly details: Readonly<IManagedNftCapacity> | null;
|
|
11
|
+
constructor(code: string, reason?: string | null, details?: Readonly<IManagedNftCapacity> | null);
|
|
5
12
|
}
|
|
13
|
+
/** Maps a failed native request. An `INVALID` or `EXHAUSTED` response keeps the
|
|
14
|
+
* bounded native refusal text and, for `EXHAUSTED`, its bound; a refusal outside
|
|
15
|
+
* that shape is `PROTOCOL`. Other responses keep only their code, and a failure
|
|
16
|
+
* without a native code is `UNAVAILABLE`. */
|
|
17
|
+
export declare function nativeError(error: unknown): ManagedNftablesError;
|
|
6
18
|
/** Bounds of the private native IPC. A status carries up to three complete policies (applied,
|
|
7
19
|
* pending previous and pending target); a policy with the maximum 1024 host grants serializes to
|
|
8
20
|
* about 111 KB and 5,300 values, so these bounds hold three of them with room for the rest of a
|
|
@@ -1,10 +1,49 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
|
+
/** `INVALID`: the input is outside the contract. `EXHAUSTED`: the policy exceeds a
|
|
3
|
+
* capacity bound of the atomic replacement budget, and a smaller policy may fit.
|
|
4
|
+
* Both carry the native refusal text in `reason`; `EXHAUSTED` also names its
|
|
5
|
+
* bound in `details`. Every other code, and the facade's own rejections, carry
|
|
6
|
+
* neither. */
|
|
2
7
|
export class ManagedNftablesError extends Error {
|
|
3
|
-
constructor(code) {
|
|
4
|
-
super(`Managed nftables ${code}.`);
|
|
8
|
+
constructor(code, reason = null, details = null) {
|
|
9
|
+
super(reason === null ? `Managed nftables ${code}.` : `Managed nftables ${code}: ${reason}`);
|
|
5
10
|
this.code = code;
|
|
11
|
+
this.reason = reason;
|
|
12
|
+
this.details = details;
|
|
6
13
|
}
|
|
7
14
|
}
|
|
15
|
+
const capacityBounds = new Set(['ruleBytes', 'targetBytes', 'operations',
|
|
16
|
+
'endpoints', 'links', 'rules', 'grants', 'hostGrants', 'workloadGrants', 'publishedPorts', 'localTcpPortOwners',
|
|
17
|
+
'restoreRules', 'restoreBytes']);
|
|
18
|
+
function capacity(value) {
|
|
19
|
+
if (typeof value !== 'object' || value === null || Object.getPrototypeOf(value) !== Object.prototype
|
|
20
|
+
|| Object.keys(value).sort().join(',') !== 'actual,bound,limit')
|
|
21
|
+
return null;
|
|
22
|
+
const { bound, limit, actual } = value;
|
|
23
|
+
if (typeof bound !== 'string' || !capacityBounds.has(bound) || typeof limit !== 'number' || typeof actual !== 'number'
|
|
24
|
+
|| !Number.isSafeInteger(limit) || !Number.isSafeInteger(actual) || limit < 0 || actual <= limit)
|
|
25
|
+
return null;
|
|
26
|
+
return Object.freeze({ bound: bound, limit, actual });
|
|
27
|
+
}
|
|
28
|
+
/** Maps a failed native request. An `INVALID` or `EXHAUSTED` response keeps the
|
|
29
|
+
* bounded native refusal text and, for `EXHAUSTED`, its bound; a refusal outside
|
|
30
|
+
* that shape is `PROTOCOL`. Other responses keep only their code, and a failure
|
|
31
|
+
* without a native code is `UNAVAILABLE`. */
|
|
32
|
+
export function nativeError(error) {
|
|
33
|
+
if (!(error instanceof plugins.smartrust.RustBridgeRequestError))
|
|
34
|
+
return new ManagedNftablesError('UNAVAILABLE');
|
|
35
|
+
const { responseErrorCode: code, message: reason, responseErrorData: data } = error;
|
|
36
|
+
if (typeof code !== 'string' || !/^[A-Z_]{1,64}$/.test(code))
|
|
37
|
+
return new ManagedNftablesError('UNAVAILABLE');
|
|
38
|
+
if (code !== 'INVALID' && code !== 'EXHAUSTED')
|
|
39
|
+
return new ManagedNftablesError(code);
|
|
40
|
+
if (!/^[\x20-\x7e]{1,256}$/.test(reason))
|
|
41
|
+
return new ManagedNftablesError('PROTOCOL');
|
|
42
|
+
if (code === 'INVALID')
|
|
43
|
+
return data === undefined ? new ManagedNftablesError(code, reason) : new ManagedNftablesError('PROTOCOL');
|
|
44
|
+
const details = capacity(data);
|
|
45
|
+
return details ? new ManagedNftablesError(code, reason, details) : new ManagedNftablesError('PROTOCOL');
|
|
46
|
+
}
|
|
8
47
|
/** Bounds of the private native IPC. A status carries up to three complete policies (applied,
|
|
9
48
|
* pending previous and pending target); a policy with the maximum 1024 host grants serializes to
|
|
10
49
|
* about 111 KB and 5,300 values, so these bounds hold three of them with room for the rest of a
|
|
@@ -275,21 +314,22 @@ export class ManagedNftables {
|
|
|
275
314
|
const owned = work.catch((error) => {
|
|
276
315
|
if (this.#state !== 'closing')
|
|
277
316
|
this.#state = 'failed-owned';
|
|
278
|
-
const
|
|
279
|
-
// Only a native compiler rejection or explicit INVALID
|
|
280
|
-
// effect-free validation
|
|
317
|
+
const failure = nativeError(error);
|
|
318
|
+
// Only a native compiler rejection or explicit INVALID or EXHAUSTED
|
|
319
|
+
// response proves effect-free validation: the native owner refuses both
|
|
320
|
+
// before any kernel work. Other native errors can follow lost ownership
|
|
281
321
|
// or admitted kernel work. Recovery stays available; confidence is lost.
|
|
282
322
|
const validation = error instanceof plugins.smartrust.RustBridgeRequestError
|
|
283
323
|
&& error.code === 'ERR_RUST_BRIDGE_REQUEST_RUST_RESPONSE'
|
|
284
|
-
&& (method === 'preparePolicy' || code === 'INVALID');
|
|
324
|
+
&& (method === 'preparePolicy' || failure.code === 'INVALID' || failure.code === 'EXHAUSTED');
|
|
285
325
|
if (!validation && (this.#state !== 'closing'
|
|
286
326
|
|| ['reconcilePolicy', 'releasePolicy', 'releasePolicyTransition', 'detachPolicy'].includes(method)))
|
|
287
327
|
this.#fail();
|
|
288
|
-
throw
|
|
328
|
+
throw failure;
|
|
289
329
|
});
|
|
290
330
|
this.#work = owned;
|
|
291
331
|
void owned.then(() => { this.#work = null; }, () => { this.#work = null; });
|
|
292
332
|
return owned;
|
|
293
333
|
}
|
|
294
334
|
}
|
|
295
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5tYW5hZ2VkbmZ0YWJsZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLm1hbmFnZWRuZnRhYmxlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUl4QyxNQUFNLE9BQU8sb0JBQXFCLFNBQVEsS0FBSztJQUM3QyxZQUE0QixJQUFZO1FBQUksS0FBSyxDQUFDLG9CQUFvQixJQUFJLEdBQUcsQ0FBQyxDQUFDO1FBQW5ELFNBQUksR0FBSixJQUFJLENBQVE7SUFBd0MsQ0FBQztDQUNsRjtBQUVEOzs7a0JBR2tCO0FBQ2xCLE1BQU0sQ0FBQyxNQUFNLGVBQWUsR0FBRyxTQUFTLENBQUM7QUFDekMsTUFBTSxhQUFhLEdBQUcsU0FBUyxDQUFDO0FBQ2hDLE1BQU0sY0FBYyxHQUFHLE1BQU0sQ0FBQztBQUU5QixvRkFBb0Y7QUFDcEYsTUFBTSxVQUFVLFFBQVEsQ0FBSSxLQUFRO0lBQ2xDLElBQUksS0FBSyxHQUFHLENBQUMsQ0FBQztJQUNkLElBQUksV0FBVyxHQUFHLENBQUMsQ0FBQztJQUNwQixNQUFNLE1BQU0sR0FBRyxDQUFDLEtBQWEsRUFBVSxFQUFFO1FBQ3ZDLElBQUksS0FBSyxDQUFDLE1BQU0sR0FBRyxhQUFhLElBQUksQ0FBQyxXQUFXLElBQUksTUFBTSxDQUFDLFVBQVUsQ0FBQyxLQUFLLENBQUMsQ0FBQyxHQUFHLGFBQWE7WUFBRSxNQUFNLElBQUksb0JBQW9CLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDekksT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDLENBQUM7SUFDRixNQUFNLElBQUksR0FBRyxDQUFDLEtBQWMsRUFBRSxLQUFhLEVBQVcsRUFBRTtRQUN0RCxJQUFJLEVBQUUsS0FBSyxHQUFHLGNBQWMsSUFBSSxLQUFLLEdBQUcsRUFBRTtZQUFFLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN0RixJQUFJ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
|
|
335
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5tYW5hZ2VkbmZ0YWJsZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9jbGFzc2VzLm1hbmFnZWRuZnRhYmxlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUt4Qzs7OztjQUljO0FBQ2QsTUFBTSxPQUFPLG9CQUFxQixTQUFRLEtBQUs7SUFDN0MsWUFBNEIsSUFBWSxFQUFrQixTQUF3QixJQUFJLEVBQ3BFLFVBQWdELElBQUk7UUFDcEUsS0FBSyxDQUFDLE1BQU0sS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLG9CQUFvQixJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUMsb0JBQW9CLElBQUksS0FBSyxNQUFNLEVBQUUsQ0FBQyxDQUFDO1FBRm5FLFNBQUksR0FBSixJQUFJLENBQVE7UUFBa0IsV0FBTSxHQUFOLE1BQU0sQ0FBc0I7UUFDcEUsWUFBTyxHQUFQLE9BQU8sQ0FBNkM7SUFFdEUsQ0FBQztDQUNGO0FBRUQsTUFBTSxjQUFjLEdBQXdCLElBQUksR0FBRyxDQUEyQixDQUFDLFdBQVcsRUFBRSxhQUFhLEVBQUUsWUFBWTtJQUNySCxXQUFXLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxRQUFRLEVBQUUsWUFBWSxFQUFFLGdCQUFnQixFQUFFLGdCQUFnQixFQUFFLG9CQUFvQjtJQUMvRyxjQUFjLEVBQUUsY0FBYyxDQUFDLENBQUMsQ0FBQztBQUVuQyxTQUFTLFFBQVEsQ0FBQyxLQUFjO0lBQzlCLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksTUFBTSxDQUFDLGNBQWMsQ0FBQyxLQUFLLENBQUMsS0FBSyxNQUFNLENBQUMsU0FBUztXQUMvRixNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksRUFBRSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsS0FBSyxvQkFBb0I7UUFBRSxPQUFPLElBQUksQ0FBQztJQUMvRSxNQUFNLEVBQUUsS0FBSyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsR0FBRyxLQUFnQyxDQUFDO0lBQ2xFLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLENBQUMsY0FBYyxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksT0FBTyxNQUFNLEtBQUssUUFBUTtXQUNqSCxDQUFDLE1BQU0sQ0FBQyxhQUFhLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsYUFBYSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEtBQUssR0FBRyxDQUFDLElBQUksTUFBTSxJQUFJLEtBQUs7UUFBRSxPQUFPLElBQUksQ0FBQztJQUNoSCxPQUFPLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBRSxLQUFLLEVBQUUsS0FBaUMsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQztBQUNwRixDQUFDO0FBRUQ7Ozs2Q0FHNkM7QUFDN0MsTUFBTSxVQUFVLFdBQVcsQ0FBQyxLQUFjO0lBQ3hDLElBQUksQ0FBQyxDQUFDLEtBQUssWUFBWSxPQUFPLENBQUMsU0FBUyxDQUFDLHNCQUFzQixDQUFDO1FBQUUsT0FBTyxJQUFJLG9CQUFvQixDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQ2pILE1BQU0sRUFBRSxpQkFBaUIsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLE1BQU0sRUFBRSxpQkFBaUIsRUFBRSxJQUFJLEVBQUUsR0FBRyxLQUFLLENBQUM7SUFDcEYsSUFBSSxPQUFPLElBQUksS0FBSyxRQUFRLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDO1FBQUUsT0FBTyxJQUFJLG9CQUFvQixDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBQzdHLElBQUksSUFBSSxLQUFLLFNBQVMsSUFBSSxJQUFJLEtBQUssV0FBVztRQUFFLE9BQU8sSUFBSSxvQkFBb0IsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN0RixJQUFJLENBQUMsc0JBQXNCLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUFFLE9BQU8sSUFBSSxvQkFBb0IsQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUN0RixJQUFJLElBQUksS0FBSyxTQUFTO1FBQUUsT0FBTyxJQUFJLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FBQyxJQUFJLG9CQUFvQixDQUFDLElBQUksRUFBRSxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxvQkFBb0IsQ0FBQyxVQUFVLENBQUMsQ0FBQztJQUNsSSxNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDL0IsT0FBTyxPQUFPLENBQUMsQ0FBQyxDQUFDLElBQUksb0JBQW9CLENBQUMsSUFBSSxFQUFFLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxvQkFBb0IsQ0FBQyxVQUFVLENBQUMsQ0FBQztBQUMxRyxDQUFDO0FBRUQ7OztrQkFHa0I7QUFDbEIsTUFBTSxDQUFDLE1BQU0sZUFBZSxHQUFHLFNBQVMsQ0FBQztBQUN6QyxNQUFNLGFBQWEsR0FBRyxTQUFTLENBQUM7QUFDaEMsTUFBTSxjQUFjLEdBQUcsTUFBTSxDQUFDO0FBRTlCLG9GQUFvRjtBQUNwRixNQUFNLFVBQVUsUUFBUSxDQUFJLEtBQVE7SUFDbEMsSUFBSSxLQUFLLEdBQUcsQ0FBQyxDQUFDO0lBQ2QsSUFBSSxXQUFXLEdBQUcsQ0FBQyxDQUFDO0lBQ3BCLE1BQU0sTUFBTSxHQUFHLENBQUMsS0FBYSxFQUFVLEVBQUU7UUFDdkMsSUFBSSxLQUFLLENBQUMsTUFBTSxHQUFHLGFBQWEsSUFBSSxDQUFDLFdBQVcsSUFBSSxNQUFNLENBQUMsVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDLEdBQUcsYUFBYTtZQUFFLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN6SSxPQUFPLEtBQUssQ0FBQztJQUNmLENBQUMsQ0FBQztJQUNGLE1BQU0sSUFBSSxHQUFHLENBQUMsS0FBYyxFQUFFLEtBQWEsRUFBVyxFQUFFO1FBQ3RELElBQUksRUFBRSxLQUFLLEdBQUcsY0FBYyxJQUFJLEtBQUssR0FBRyxFQUFFO1lBQUUsTUFBTSxJQUFJLG9CQUFvQixDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ3RGLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUTtZQUFFLE9BQU8sTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ3BELElBQUksS0FBSyxLQUFLLElBQUksSUFBSSxPQUFPLEtBQUssS0FBSyxTQUFTO1lBQUUsT0FBTyxLQUFLLENBQUM7UUFDL0QsSUFBSSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksTUFBTSxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUM7WUFBRSxPQUFPLEtBQUssQ0FBQztRQUMzRSxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDO1lBQUUsTUFBTSxJQUFJLG9CQUFvQixDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzlHLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDbkMsSUFBSSxNQUFNLENBQUMsY0FBYyxDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDO1lBQUUsTUFBTSxJQUFJLG9CQUFvQixDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQzdILE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDcEMsSUFBSSxJQUFJLENBQUMsTUFBTSxHQUFHLGNBQWMsR0FBRyxLQUFLO1lBQUUsTUFBTSxJQUFJLG9CQUFvQixDQUFDLFNBQVMsQ0FBQyxDQUFDO1FBQ3BGLEtBQUssTUFBTSxHQUFHLElBQUksSUFBSTtZQUFFLElBQUksT0FBTyxHQUFHLEtBQUssUUFBUTtnQkFBRSxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUM7UUFDakUsTUFBTSxXQUFXLEdBQUcsTUFBTSxDQUFDLHlCQUF5QixDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQzVELE1BQU0sTUFBTSxHQUF3QyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO1FBQ3BFLElBQUksS0FBSyxJQUFJLElBQUksQ0FBQyxNQUFNLEtBQU0sS0FBbUIsQ0FBQyxNQUFNLEdBQUcsQ0FBQztZQUFFLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxTQUFTLENBQUMsQ0FBQztRQUN4RyxLQUFLLE1BQU0sR0FBRyxJQUFJLElBQUksRUFBRSxDQUFDO1lBQ3ZCLElBQUksS0FBSyxJQUFJLEdBQUcsS0FBSyxRQUFRO2dCQUFFLFNBQVM7WUFDeEMsTUFBTSxVQUFVLEdBQUcsV0FBVyxDQUFDLEdBQWEsQ0FBQyxDQUFDO1lBQzlDLElBQUksT0FBTyxHQUFHLEtBQUssUUFBUSxJQUFJLENBQUMsVUFBVSxDQUFDLFVBQVUsSUFBSSxDQUFDLENBQUMsT0FBTyxJQUFJLFVBQVUsQ0FBQzttQkFDNUUsS0FBSyxJQUFJLENBQUMsbUJBQW1CLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQztnQkFBRSxNQUFNLElBQUksb0JBQW9CLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDeEYsTUFBTSxDQUFDLGNBQWMsQ0FBQyxNQUFNLEVBQUUsR0FBRyxFQUFFLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxVQUFVLENBQUMsS0FBSyxFQUFFLEtBQUssR0FBRyxDQUFDLENBQUMsRUFBRSxVQUFVLEVBQUUsSUFBSSxFQUFFLFlBQVksRUFBRSxJQUFJLEVBQUUsUUFBUSxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7UUFDekksQ0FBQztRQUNELE9BQU8sTUFBTSxDQUFDO0lBQ2hCLENBQUMsQ0FBQztJQUNGLE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFNLENBQUM7SUFDbkMsSUFBSSxNQUFNLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUMsR0FBRyxhQUFhO1FBQUUsTUFBTSxJQUFJLG9CQUFvQixDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ3pHLE9BQU8sTUFBTSxDQUFDO0FBQ2hCLENBQUM7QUFFRCxzR0FBc0c7QUFDdEcsTUFBTSxPQUFPLGVBQWU7SUFDakIsUUFBUSxDQUErQjtJQUNoRCxPQUFPLEdBQXNFLElBQUksQ0FBQztJQUNsRixNQUFNLEdBQTBFLEtBQUssQ0FBQztJQUN0RixLQUFLLEdBQTRCLElBQUksQ0FBQztJQUN0QyxNQUFNLEdBQXlCLElBQUksQ0FBQztJQUNwQyxVQUFVLEdBQStCLElBQUksQ0FBQztJQUM5Qyx1QkFBdUIsR0FBRyxLQUFLLENBQUM7SUFDaEMsYUFBYSxHQUFHLEtBQUssQ0FBQztJQUN0QixVQUFVLEdBQUcsS0FBSyxDQUFDO0lBQ25CLGVBQWUsR0FBNkMsSUFBSSxDQUFDO0lBQ2pFLE9BQU8sR0FBdUMsSUFBSSxDQUFDO0lBQ25ELGtCQUFrQixHQUE4RCxJQUFJLENBQUM7SUFDNUUsUUFBUSxHQUFHLElBQUksZUFBZSxFQUFFLENBQUM7SUFDMUMsWUFBWSxHQUFHLEtBQUssQ0FBQztJQUVyQixZQUFZLE9BQTJCO1FBQ3JDLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2xDLElBQUksTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxFQUFFLFlBQVksRUFBRSxXQUFXLEVBQUUsWUFBWSxFQUFFLG9CQUFvQixDQUFDLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDO2VBQ2xJLENBQUMsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLE9BQU8sRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLHVCQUF1QixDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztlQUM5SixJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsQ0FBQyxNQUFNLEdBQUcsRUFBRSxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxTQUFTLENBQUMsVUFBVSxDQUFDLE9BQU8sQ0FBQztlQUNwRixJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVUsS0FBSyxTQUFTLElBQUksQ0FBQyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsVUFBVSxDQUFDLElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUMvSSxNQUFNLElBQUksb0JBQW9CLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDNUMsQ0FBQztRQUNELElBQUksSUFBSSxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsS0FBSyxTQUFTLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxLQUFLLE9BQU87ZUFDOUUsQ0FBQyxNQUFNLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsa0JBQWtCLENBQUMsSUFBSSxJQUFJLENBQUMsUUFBUSxDQUFDLGtCQUFrQixHQUFHLENBQUM7ZUFDM0YsSUFBSSxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsR0FBRyxXQUFXLENBQUM7WUFBRSxNQUFNLElBQUksb0JBQW9CLENBQUMsU0FBUyxDQUFDLENBQUM7SUFDbEcsQ0FBQztJQUVELElBQVcsS0FBSyxLQUFhLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFFbEQ7OzhFQUUwRTtJQUMxRSxJQUFXLGFBQWEsS0FBa0IsT0FBTyxJQUFJLENBQUMsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7SUFFeEUsS0FBSztRQUNILElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxTQUFTO1lBQUUsSUFBSSxDQUFDLE1BQU0sR0FBRyxjQUFjLENBQUM7UUFDNUQsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLE9BQU87WUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxJQUFJLG9CQUFvQixDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUM7SUFDakcsQ0FBQztJQUVELHVGQUF1RjtJQUNoRixLQUFLO1FBQ1YsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLEtBQUssSUFBSSxJQUFJLENBQUMsVUFBVTtZQUFFLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLG9CQUFvQixDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUM7UUFDN0csSUFBSSxDQUFDLE1BQU0sR0FBRyxVQUFVLENBQUM7UUFDekIsTUFBTSxJQUFJLEdBQUcsQ0FBQyxLQUFLLElBQUksRUFBRTtZQUN2QixJQUFJLE9BQU8sQ0FBQyxRQUFRLEtBQUssT0FBTyxJQUFJLENBQUMsQ0FBQyxLQUFLLEVBQUUsT0FBTyxDQUFDLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUM7Z0JBQUUsTUFBTSxJQUFJLG9CQUFvQixDQUFDLHNCQUFzQixDQUFDLENBQUM7WUFDckksTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsSUFBSSxHQUFHLENBQUMsS0FBSyxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztZQUN4RSxJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQyxVQUFVLENBQStCO2dCQUM1RSxVQUFVLEVBQUUsZUFBZSxFQUFFLFVBQVUsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLFVBQVU7dUJBQzVELE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxXQUFXLEVBQUUsdUJBQXVCLE9BQU8sQ0FBQyxJQUFJLEtBQUssS0FBSyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE9BQU8sT0FBTyxDQUFDO2dCQUNuSCxVQUFVLEVBQUUsRUFBRSxFQUFFLGdCQUFnQixFQUFFLEtBQUs7Z0JBQ3ZDLE9BQU8sRUFBRSxDQUFDLGNBQWMsRUFBRSxHQUFHLENBQUMsSUFBSSxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsS0FBSyxTQUFTLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyx3QkFBd0IsRUFBRSxHQUFHLENBQUMsQ0FBQyxDQUFDO2dCQUNySCx3QkFBd0IsRUFBRSxJQUFJLENBQUMsUUFBUSxDQUFDLGtCQUFrQixLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsa0JBQWtCLENBQUM7Z0JBQ3pILGNBQWMsRUFBRSxJQUFJLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxFQUFFLGNBQWMsRUFBRSxlQUFlO2dCQUMvRSwwQkFBMEIsRUFBRSxFQUFFLGFBQWEsRUFBRSxDQUFDLEVBQUUsU0FBUyxFQUFFLENBQUMsRUFBRSxlQUFlLEVBQUUsQ0FBQyxFQUFFLGFBQWEsRUFBRSxDQUFDLEVBQUUsYUFBYSxFQUFFLENBQUMsRUFBRSx1QkFBdUIsRUFBRSxDQUFDLEVBQUUsWUFBWSxFQUFFLENBQUMsRUFBRSxVQUFVLEVBQUUsQ0FBQyxFQUFFO2FBQ25MLENBQUMsQ0FBQztZQUNILElBQUksQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxHQUFHLEVBQUU7Z0JBQzNCLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBSSxDQUFDO2dCQUN6QixJQUFJLENBQUMsQ0FBQyxTQUFTLEVBQUUsU0FBUyxDQUFDLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7b0JBQUUsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ2xFLENBQUMsQ0FBQyxDQUFDO1lBQ0gsSUFBSSxDQUFDLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxLQUFLLEVBQUU7Z0JBQUUsTUFBTSxJQUFJLG9CQUFvQixDQUFDLGFBQWEsQ0FBQyxDQUFDO1lBQy9FLE1BQU0sRUFBRSxPQUFPLEVBQUUsVUFBVSxFQUFFLFNBQVMsRUFBRSxHQUFHLElBQUksQ0FBQyxRQUFRLENBQUM7WUFDekQsTUFBTSxNQUFNLEdBQUcsUUFBUSxDQUFDLE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsV0FBVyxFQUFFLEVBQUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxTQUFTLEVBQUUsQ0FBQyxDQUFDLENBQUM7WUFDekcsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLFVBQVU7Z0JBQUUsTUFBTSxJQUFJLG9CQUFvQixDQUFDLGFBQWEsQ0FBQyxDQUFDO1lBQzlFLElBQUksQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDO1lBQUMsT0FBTyxNQUFNLENBQUM7UUFDdkMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNMLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEVBQUUsV0FBVyxDQUFDLENBQUM7SUFDeEMsQ0FBQztJQUVNLE9BQU8sQ0FBQyxNQUFlO1FBQzVCLE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxlQUFlLEVBQUUsTUFBTSxDQUFDLENBQUM7SUFDL0MsQ0FBQztJQUNNLFNBQVMsQ0FBQyxVQUEwQztRQUN6RCxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMsaUJBQWlCLEVBQUUsVUFBVSxDQUFDLENBQUM7SUFDckQsQ0FBQztJQUNNLE9BQU8sS0FBMEMsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDLGVBQWUsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDNUYsT0FBTyxDQUFDLFFBQTJDO1FBQ3hELE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxlQUFlLEVBQUUsUUFBUSxDQUFDLENBQUM7SUFDakQsQ0FBQztJQUVEOzs7O21GQUkrRTtJQUN4RSxpQkFBaUIsQ0FBQyxPQUE4QztRQUNyRSxPQUFPLElBQUksQ0FBQyxPQUFPLENBQUMseUJBQXlCLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDMUQsQ0FBQztJQUVEOztnRkFFNEU7SUFDckUsTUFBTSxDQUFDLFFBQTJDO1FBQ3ZELElBQUksSUFBSSxDQUFDLHVCQUF1QixJQUFJLElBQUksQ0FBQyxVQUFVLEtBQUssUUFBUSxJQUFJLElBQUksQ0FBQyxrQkFBa0I7WUFBRSxPQUFPLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxvQkFBb0IsQ0FBQyxhQUFhLENBQUMsQ0FBQyxDQUFDO1FBQzVKLElBQUksQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDO1FBQ3ZCLElBQUksUUFBMkMsQ0FBQztRQUNoRCxJQUFJLENBQUM7WUFDSCxRQUFRLEdBQUcsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFDO1lBQzlCLElBQUksSUFBSSxDQUFDLGVBQWUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLGVBQWUsRUFBRSxRQUFRLENBQUM7Z0JBQUUsTUFBTSxJQUFJLG9CQUFvQixDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQ3hJLElBQUksSUFBSSxDQUFDLE9BQU87Z0JBQUUsT0FBTyxJQUFJLENBQUMsT0FBTyxDQUFDO1lBQ3RDLElBQUksQ0FBQyxDQUFDLE9BQU8sRUFBRSxjQUFjLENBQUMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUMsS0FBSyxJQUFJLElBQUksQ0FBQyxNQUFNO2dCQUFFLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxhQUFhLENBQUMsQ0FBQztZQUNsSixJQUFJLENBQUMsZUFBZSxHQUFHLFFBQVEsQ0FBQztRQUNsQyxDQUFDO1FBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztZQUFDLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLFlBQVksb0JBQW9CLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsSUFBSSxvQkFBb0IsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO1FBQUMsQ0FBQztRQUMvSCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLGNBQWMsRUFBRSxRQUFRLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRTtZQUN6RixNQUFNLE1BQU0sR0FBRyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7WUFDL0IsSUFBSSxNQUFNLEVBQUUsUUFBUSxLQUFLLElBQUksSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxDQUFDLE1BQU0sS0FBSyxDQUFDO2dCQUFFLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUM5RyxJQUFJLENBQUMsYUFBYSxHQUFHLEtBQUssQ0FBQztZQUMzQixPQUFPLE1BQU0sQ0FBQztRQUNoQixDQUFDLENBQUMsRUFBRSxjQUFjLENBQUMsQ0FBQztRQUNwQixNQUFNLFFBQVEsR0FBRyxDQUFDLEtBQUssSUFBSSxFQUFFLEdBQUcsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsQ0FBQyxNQUFNLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDLE9BQU8sTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQztRQUNuRyxJQUFJLENBQUMsT0FBTyxHQUFHLFFBQVEsQ0FBQztRQUN4QixLQUFLLFFBQVEsQ0FBQyxLQUFLLENBQUMsR0FBRyxFQUFFLEdBQUcsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUNwRCxPQUFPLFFBQVEsQ0FBQztJQUNsQixDQUFDO0lBRUQ7Ozs7MkVBSXVFO0lBQ2hFLGNBQWM7UUFDbkIsSUFBSSxJQUFJLENBQUMsVUFBVSxLQUFLLFFBQVE7WUFBRSxPQUFPLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxvQkFBb0IsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDO1FBQzlGLElBQUksQ0FBQyx1QkFBdUIsR0FBRyxJQUFJLENBQUM7UUFDcEMsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLENBQUM7UUFDdkIsT0FBTyxJQUFJLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDdEIsQ0FBQztJQUVEO21HQUMrRjtJQUN4RixLQUFLO1FBQ1YsSUFBSSxJQUFJLENBQUMsTUFBTTtZQUFFLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQztRQUNwQyxJQUFJLElBQUksQ0FBQyxNQUFNLEtBQUssU0FBUztZQUFFLE9BQU8sT0FBTyxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ3hELElBQUksQ0FBQyxVQUFVLEtBQUssSUFBSSxDQUFDLFVBQVUsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLENBQUMsQ0FBQyxRQUFRLENBQUM7UUFDMUQsTUFBTSxTQUFTLEdBQUcsSUFBSSxDQUFDLFVBQVUsS0FBSyxRQUFRLENBQUM7UUFDL0MsSUFBSSxDQUFDLE1BQU0sR0FBRyxTQUFTLENBQUM7UUFDeEIsSUFBSSxDQUFDLE1BQU0sR0FBRyxDQUFDLEtBQUssSUFBSSxFQUFFO1lBQ3hCLE1BQU0sSUFBSSxDQUFDLEtBQUssRUFBRSxLQUFLLENBQUMsR0FBRyxFQUFFLENBQUMsU0FBUyxDQUFDLENBQUM7WUFDekMsSUFBSSxJQUFJLENBQUMsT0FBTyxFQUFFLENBQUM7Z0JBQ2pCLElBQUksSUFBSSxDQUFDLGFBQWEsSUFBSSxDQUFDLFNBQVMsRUFBRSxDQUFDO29CQUNyQyxvRUFBb0U7b0JBQ3BFLG9FQUFvRTtvQkFDcEUsb0VBQW9FO29CQUNwRSxNQUFNLE1BQU0sR0FBRyxJQUFJLENBQUMsa0JBQWtCO3dCQUNwQyxDQUFDLENBQUMsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyx5QkFBeUIsRUFBRSxJQUFJLENBQUMsa0JBQWtCLENBQUMsT0FBTyxDQUFDO3dCQUM1RixDQUFDLENBQUMsTUFBTSxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxZQUFZLEVBQUUsRUFBRSxDQUFDLENBQUM7b0JBQ3JELElBQUksTUFBTSxDQUFDLFFBQVEsS0FBSyxJQUFJLElBQUksSUFBSSxDQUFDLGtCQUFrQixJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUMsTUFBTSxLQUFLLENBQUM7d0JBQUUsTUFBTSxJQUFJLG9CQUFvQixDQUFDLFVBQVUsQ0FBQyxDQUFDO2dCQUMxSSxDQUFDO2dCQUNELE1BQU0sSUFBSSxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsRUFBRSxhQUFhLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztZQUN4RCxDQUFDO1lBQ0QsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLENBQUM7WUFBQyxJQUFJLENBQUMsTUFBTSxHQUFHLFNBQVMsQ0FBQztRQUMvQyxDQUFDLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxHQUFHLEVBQUU7WUFDZCxJQUFJLENBQUMsTUFBTSxHQUFHLGNBQWMsQ0FBQztZQUFDLElBQUksQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDO1lBQ2pELElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUNiLE1BQU0sSUFBSSxvQkFBb0IsQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDO1FBQ3hELENBQUMsQ0FBQyxDQUFDO1FBQ0gsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDO0lBQ3JCLENBQUM7SUFFRCxPQUFPLENBQStDLE1BQVMsRUFBRSxNQUFpRDtRQUNoSCxJQUFJLElBQUksQ0FBQyxVQUFVLElBQUksQ0FBQyxDQUFDLE9BQU8sRUFBRSxjQUFjLENBQUMsQ0FBQyxRQUFRLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sSUFBSSxJQUFJLENBQUMsS0FBSztZQUFFLE9BQU8sT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLG9CQUFvQixDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUM7UUFDdkssSUFBSSxRQUFtRCxDQUFDO1FBQ3hELElBQUksQ0FBQztZQUFDLFFBQVEsR0FBRyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7UUFBQyxDQUFDO1FBQUMsTUFBTSxDQUFDO1lBQUMsT0FBTyxPQUFPLENBQUMsTUFBTSxDQUFDLElBQUksb0JBQW9CLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztRQUFDLENBQUM7UUFDMUcsSUFBSSxJQUFJLENBQUMsa0JBQWtCLElBQUksQ0FBQyxNQUFNLEtBQUssZUFBZSxJQUFJLE1BQU0sS0FBSyx5QkFBeUI7ZUFDN0YsTUFBTSxLQUFLLHlCQUF5QixJQUFJLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsa0JBQWtCLENBQUMsT0FBTyxFQUFFLFFBQVEsQ0FBQyxDQUFDLEVBQUUsQ0FBQztZQUN6SCxPQUFPLE9BQU8sQ0FBQyxNQUFNLENBQUMsSUFBSSxvQkFBb0IsQ0FBQyxVQUFVLENBQUMsQ0FBQyxDQUFDO1FBQzlELENBQUM7UUFDRCxJQUFJLE1BQU0sS0FBSyx5QkFBeUI7WUFBRSxJQUFJLENBQUMsa0JBQWtCLEdBQUcsRUFBRSxPQUFPLEVBQUUsUUFBaUQsRUFBRSxDQUFDO1FBQ25JLElBQUksTUFBTSxLQUFLLGlCQUFpQixJQUFJLE1BQU0sS0FBSyxlQUFlLElBQUksTUFBTSxLQUFLLHlCQUF5QjtZQUFFLElBQUksQ0FBQyxhQUFhLEdBQUcsSUFBSSxDQUFDO1FBQ2xJLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxNQUFNLEVBQUUsUUFBUSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxFQUFFLEVBQUU7WUFDNUUsTUFBTSxjQUFjLEdBQUcsUUFBUSxDQUFDLE1BQU0sQ0FBQyxDQUFDO1lBQ3hDLElBQUksTUFBTSxLQUFLLGVBQWUsSUFBSSxNQUFNLEtBQUsseUJBQXlCLEVBQUUsQ0FBQztnQkFDdkUsSUFBSSxNQUFNLEtBQUsseUJBQXlCLElBQUksQ0FBRSxjQUFxQyxFQUFFLFFBQVEsS0FBSyxJQUFJO3VCQUNqRyxNQUFNLENBQUMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxDQUFDLE1BQU0sS0FBSyxDQUFDLENBQUM7b0JBQUUsTUFBTSxJQUFJLG9CQUFvQixDQUFDLFVBQVUsQ0FBQyxDQUFDO2dCQUMzRixJQUFJLENBQUMsYUFBYSxHQUFHLEtBQUssQ0FBQztZQUM3QixDQUFDO1lBQ0QsSUFBSSxNQUFNLEtBQUssZUFBZSxFQUFFLENBQUM7Z0JBQy9CLE1BQU0sTUFBTSxHQUFHLGNBQTRDLENBQUM7Z0JBQzVELElBQUksTUFBTSxDQUFDLEtBQUssS0FBSyxjQUFjLElBQUksTUFBTSxDQUFDLEtBQUssS0FBSyxJQUFJO29CQUFFLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztZQUM3RSxDQUFDO1lBQ0QsSUFBSSxDQUFDLElBQUksQ0FBQyxZQUFZLElBQUksSUFBSSxDQUFDLE1BQU0sS0FBSyxTQUFTLElBQUksQ0FBQyxNQUFNLEtBQUssaUJBQWlCLElBQUksTUFBTSxLQUFLLGVBQWUsSUFBSSxNQUFNLEtBQUsseUJBQXlCO21CQUNySixNQUFNLEtBQUssZUFBZSxJQUFLLGNBQTZDLENBQUMsS0FBSyxLQUFLLElBQUk7dUJBQ3hGLGNBQTZDLENBQUMsS0FBSyxLQUFLLGNBQWMsQ0FBQztnQkFBRSxJQUFJLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQztZQUN2RyxPQUFPLGNBQWMsQ0FBQztRQUN4QixDQUFDLENBQUMsRUFBRSxNQUFNLENBQUMsQ0FBQztJQUNkLENBQUM7SUFDRCxNQUFNLENBQUksSUFBZ0IsRUFBRSxNQUEwQztRQUNwRSxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUU7WUFDakMsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLFNBQVM7Z0JBQUUsSUFBSSxDQUFDLE1BQU0sR0FBRyxjQUFjLENBQUM7WUFDNUQsTUFBTSxPQUFPLEdBQUcsV0FBVyxDQUFDLEtBQUssQ0FBQyxDQUFDO1lBQ25DLG9FQUFvRTtZQUNwRSx3RUFBd0U7WUFDeEUsd0VBQXdFO1lBQ3hFLHlFQUF5RTtZQUN6RSxNQUFNLFVBQVUsR0FBRyxLQUFLLFlBQVksT0FBTyxDQUFDLFNBQVMsQ0FBQyxzQkFBc0I7bUJBQ3ZFLEtBQUssQ0FBQyxJQUFJLEtBQUssdUNBQXVDO21CQUN0RCxDQUFDLE1BQU0sS0FBSyxlQUFlLElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxTQUFTLElBQUksT0FBTyxDQUFDLElBQUksS0FBSyxXQUFXLENBQUMsQ0FBQztZQUNoRyxJQUFJLENBQUMsVUFBVSxJQUFJLENBQUMsSUFBSSxDQUFDLE1BQU0sS0FBSyxTQUFTO21CQUN4QyxDQUFDLGlCQUFpQixFQUFFLGVBQWUsRUFBRSx5QkFBeUIsRUFBRSxjQUFjLENBQUMsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUM7Z0JBQUUsSUFBSSxDQUFDLEtBQUssRUFBRSxDQUFDO1lBQ3JILE1BQU0sT0FBTyxDQUFDO1FBQ2hCLENBQUMsQ0FBQyxDQUFDO1FBQ0gsSUFBSSxDQUFDLEtBQUssR0FBRyxLQUFLLENBQUM7UUFDbkIsS0FBSyxLQUFLLENBQUMsSUFBSSxDQUFDLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxFQUFFLEdBQUcsRUFBRSxHQUFHLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUM7UUFDNUUsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDO0NBQ0YifQ==
|
|
@@ -80,6 +80,20 @@ export interface IManagedNftOptions {
|
|
|
80
80
|
networkNamespaceFd?: number;
|
|
81
81
|
}
|
|
82
82
|
export type TManagedNftPolicy = IManagedNftPolicy | IManagedNftPolicyV2;
|
|
83
|
+
/** Capacity bounds of the atomic replacement budget. The compiled target's
|
|
84
|
+
* `ruleBytes` (100,000), `targetBytes` (212,000) and `operations` (768), the input
|
|
85
|
+
* counts that exist to hold it (`endpoints`, `links`, `rules`, `grants`,
|
|
86
|
+
* `hostGrants`, `workloadGrants`, `publishedPorts`, `localTcpPortOwners`) and the
|
|
87
|
+
* Docker forwarding contribution's `restoreRules` (192) and `restoreBytes` (100,000). */
|
|
88
|
+
export type TManagedNftCapacityBound = 'ruleBytes' | 'targetBytes' | 'operations' | 'endpoints' | 'links' | 'rules' | 'grants' | 'hostGrants' | 'workloadGrants' | 'publishedPorts' | 'localTcpPortOwners' | 'restoreRules' | 'restoreBytes';
|
|
89
|
+
/** The bound an `EXHAUSTED` refusal names. `actual` is always above `limit`. It is
|
|
90
|
+
* exact for input counts and the schema-v1 budget; the other compiled budgets stop
|
|
91
|
+
* at the first message or rule past the limit, so the policy needs at least `actual`. */
|
|
92
|
+
export interface IManagedNftCapacity {
|
|
93
|
+
bound: TManagedNftCapacityBound;
|
|
94
|
+
limit: number;
|
|
95
|
+
actual: number;
|
|
96
|
+
}
|
|
83
97
|
export type TManagedNftCommands<TPolicy extends TManagedNftPolicy = IManagedNftPolicy> = {
|
|
84
98
|
preparePolicy: {
|
|
85
99
|
params: TPolicy;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@push.rocks/smartnftables",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "A TypeScript module for managing nftables rules including NAT, firewall, and rate limiting with a high-level API.",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"dependencies": {
|
|
21
21
|
"@push.rocks/smartlog": "^3.2.2",
|
|
22
22
|
"@push.rocks/smartpromise": "^4.2.5",
|
|
23
|
-
"@push.rocks/smartrust": "2.
|
|
23
|
+
"@push.rocks/smartrust": "2.5.0"
|
|
24
24
|
},
|
|
25
25
|
"files": [
|
|
26
26
|
"ts/**/*",
|
package/readme.md
CHANGED
|
@@ -163,7 +163,7 @@ Subscribe to `failureSignal` before starting or admitting policy work, and check
|
|
|
163
163
|
exit, ambiguous operation failures, native inspection reporting failed ownership,
|
|
164
164
|
and unconfirmed cleanup abort the signal once with `ManagedNftablesError` code
|
|
165
165
|
`OWNER_LOST`. Local input rejection, effect-free native compiler validation and
|
|
166
|
-
native `INVALID` responses do not abort it. Other native errors can occur after
|
|
166
|
+
native `INVALID` and `EXHAUSTED` responses do not abort it. Other native errors can occur after
|
|
167
167
|
kernel effects or ownership loss and conservatively abort it. Successful orderly
|
|
168
168
|
close, including a cancelled startup, does not signal an unexpected failure;
|
|
169
169
|
ambiguous mutations still signal loss while close is draining them.
|
|
@@ -253,6 +253,62 @@ workload before treating revocation as complete or reusing its addresses and
|
|
|
253
253
|
interfaces. Table deletion does not release IP allocation authority or clean up
|
|
254
254
|
conntrack/NAT state. This restriction also applies to private veth/TUN forwarding.
|
|
255
255
|
|
|
256
|
+
### Refusals: `INVALID` and `EXHAUSTED`
|
|
257
|
+
|
|
258
|
+
Every failure is a `ManagedNftablesError` with a stable `code`. Two codes are
|
|
259
|
+
refusals of the input itself, raised by the facade's capture or by the native owner
|
|
260
|
+
before any kernel work:
|
|
261
|
+
|
|
262
|
+
- `INVALID`: the input is outside the contract (schema, identifiers, addresses,
|
|
263
|
+
overlaps, authority, or a bound of the topology's shape). A smaller policy does
|
|
264
|
+
not help; the input must change.
|
|
265
|
+
- `EXHAUSTED`: the policy is too big for one atomic replacement. It exceeds the
|
|
266
|
+
compiled budget described under the router below, or an input count that exists
|
|
267
|
+
to hold that budget. A smaller policy may fit.
|
|
268
|
+
|
|
269
|
+
Both carry the native refusal text in `reason` (bounded, printable ASCII, never
|
|
270
|
+
policy content) and in the message, `Managed nftables EXHAUSTED: <reason>`.
|
|
271
|
+
`EXHAUSTED` also names its bound in `details`, `{ bound, limit, actual }`:
|
|
272
|
+
|
|
273
|
+
| `bound` | `limit` | Counts |
|
|
274
|
+
| --- | --- | --- |
|
|
275
|
+
| `ruleBytes` | 100,000 | encoded rules, chains and sets of the target |
|
|
276
|
+
| `targetBytes` | 212,000 | the complete target with its set elements |
|
|
277
|
+
| `operations` | 768 | messages of the target |
|
|
278
|
+
| `endpoints` | 32 (v1), 128 (router) | private endpoints |
|
|
279
|
+
| `links` | 128 | router links |
|
|
280
|
+
| `rules` | 128 (v1), 1024 (router) | private directed rules |
|
|
281
|
+
| `grants` | 1024 | router egress grants across every generation |
|
|
282
|
+
| `hostGrants` | 1024 | host grants of one scope |
|
|
283
|
+
| `workloadGrants` | 1024 | router workload grants |
|
|
284
|
+
| `publishedPorts` | 1024 | publications of one scope |
|
|
285
|
+
| `localTcpPortOwners` | 8 | loopback TCP port owners of the pool guard |
|
|
286
|
+
| `restoreRules` | 192 | Docker forwarding contribution rules |
|
|
287
|
+
| `restoreBytes` | 100,000 | Docker forwarding contribution command bytes |
|
|
288
|
+
|
|
289
|
+
`actual` is always above `limit`. It is exact for input counts and for the
|
|
290
|
+
schema-v1 budget, which measures its complete program. For the other compiled
|
|
291
|
+
budgets (`ruleBytes`, `targetBytes`, `operations`, `restoreRules`, `restoreBytes`)
|
|
292
|
+
compilation stops at the first message or rule past the limit, so the complete
|
|
293
|
+
policy needs at least `actual`. Input counts are checked
|
|
294
|
+
before their entries are validated, so a smaller policy can still be `INVALID`.
|
|
295
|
+
Every other bound (identifier and interface-name lengths, source prefixes per
|
|
296
|
+
endpoint, addresses per link, protected prefixes, platform endpoints, active
|
|
297
|
+
generations, handoffs and allocations, ranges per allocation, guarded pools, the
|
|
298
|
+
IPC and capture limits) describes the shape of the topology and stays `INVALID`.
|
|
299
|
+
Other codes, and the facade's own local rejections, carry neither `reason` nor
|
|
300
|
+
`details`. A native refusal outside this shape is `PROTOCOL`.
|
|
301
|
+
|
|
302
|
+
```typescript
|
|
303
|
+
try {
|
|
304
|
+
prepared = await owner.prepare(policy);
|
|
305
|
+
} catch (error) {
|
|
306
|
+
if (error instanceof ManagedNftablesError && error.code === 'EXHAUSTED' && error.details?.bound === 'publishedPorts') {
|
|
307
|
+
// Publish fewer ports and prepare again.
|
|
308
|
+
} else throw error;
|
|
309
|
+
}
|
|
310
|
+
```
|
|
311
|
+
|
|
256
312
|
### Combined router egress and host transit
|
|
257
313
|
|
|
258
314
|
`ManagedNftables<IManagedNftPolicyV2>` accepts the exported schema-v2 policy.
|
|
@@ -677,7 +733,7 @@ graph (fewer than 768 rules, chains and sets of at most 140 bytes each), and
|
|
|
677
733
|
212,000 bytes for the complete target, of which rules, chains and sets may take at
|
|
678
734
|
most 100,000 and set elements the rest. Scopes that grow with their inputs keep
|
|
679
735
|
those inputs in set elements, so the element share grows where the rule share
|
|
680
|
-
shrinks; `prepare()` refuses a target beyond either bound before any kernel work. The private facade IPC carries up to three complete policies in one
|
|
736
|
+
shrinks; `prepare()` refuses a target beyond either bound with `EXHAUSTED` before any kernel work. The private facade IPC carries up to three complete policies in one
|
|
681
737
|
status and is bounded at 1,048,576 bytes per line; the facade captures at most
|
|
682
738
|
1,000,000 JSON bytes and 65,536 values per request or result. The caller owns the host route
|
|
683
739
|
to the workload through the handoff, selecting the transit host address as the
|
|
@@ -129,7 +129,8 @@ impl Prepared {
|
|
|
129
129
|
let comment = format!("snftd1:{}:{}:{}:{}",options.owner_id,options.instance_id,self.digest.strip_prefix("sha256:").ok_or(Error::Invalid)?,result.len());
|
|
130
130
|
args.push(comment.clone());args.extend(["-j","ACCEPT"].map(String::from));
|
|
131
131
|
bytes += args.iter().map(|arg|arg.len()+1).sum::<usize>()+32;
|
|
132
|
-
|
|
132
|
+
crate::capacity("restoreRules", 192, result.len() + 1)?;
|
|
133
|
+
crate::capacity("restoreBytes", 100_000, bytes)?;
|
|
133
134
|
result.push(Rule {comment,args});
|
|
134
135
|
}
|
|
135
136
|
}
|
|
@@ -61,22 +61,20 @@ impl<'a> Program<'a> {
|
|
|
61
61
|
// Same atomic replacement reserve as v1: a previous bounded graph must
|
|
62
62
|
// fit its handle-only deletions beside the complete target, and the
|
|
63
63
|
// target's set elements, in one `wire::BATCH_BYTES` batch.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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)?;
|
|
69
71
|
self.operations.push((operation, attributes));
|
|
70
72
|
Ok(())
|
|
71
73
|
}
|
|
72
74
|
/// Element messages count against the target, beside the rules.
|
|
73
75
|
fn push_elements(&mut self, attributes: Vec<Attr>) -> Result<()> {
|
|
74
76
|
self.element_bytes += wire::encode_attrs(&attributes).len() + 20;
|
|
75
|
-
|
|
76
|
-
self.bytes + self.element_bytes <= TARGET_BYTES && self.operations.len() < OPERATIONS,
|
|
77
|
-
)?;
|
|
78
|
-
self.operations.push((12, attributes));
|
|
79
|
-
Ok(())
|
|
77
|
+
self.admit(12, attributes)
|
|
80
78
|
}
|
|
81
79
|
/// One named exact-key hash set, or a map to `data_length` bytes. CONSTANT
|
|
82
80
|
/// like every set here.
|
package/rust/src/egress.rs
CHANGED
|
@@ -484,7 +484,7 @@ pub fn destination(grant: &Grant, protection: &Protection) -> Result<String> {
|
|
|
484
484
|
/// with the leased outbound source ports translated to that same transit address.
|
|
485
485
|
fn normalize_router_published(value: &mut RouterScope) -> Result<()> {
|
|
486
486
|
let mut ports = std::mem::take(&mut value.published_ports);
|
|
487
|
-
|
|
487
|
+
crate::capacity("publishedPorts", PUBLISHED_PORTS, ports.len())?;
|
|
488
488
|
ports.sort();
|
|
489
489
|
for (index, port) in ports.iter().enumerate() {
|
|
490
490
|
require(
|
|
@@ -599,11 +599,12 @@ fn exclusive_symmetric<'a>(
|
|
|
599
599
|
/// the enclosing scope.
|
|
600
600
|
fn normalize_host_grants(
|
|
601
601
|
grants: &mut [HostGrant],
|
|
602
|
+
name: &'static str,
|
|
602
603
|
bound: impl Fn(&HostGrant) -> Result<()>,
|
|
603
604
|
) -> Result<()> {
|
|
604
605
|
// The contract maximum per projection; set lookups keep the rule graph
|
|
605
606
|
// constant, and the element budget holds this many in every scope.
|
|
606
|
-
|
|
607
|
+
crate::capacity(name, HOST_GRANTS, grants.len())?;
|
|
607
608
|
grants.sort();
|
|
608
609
|
for (index, grant) in grants.iter().enumerate() {
|
|
609
610
|
require(
|
|
@@ -648,7 +649,7 @@ fn normalize_router_host_grants(value: &mut RouterScope) -> Result<()> {
|
|
|
648
649
|
.any(|source| covers(source, &target) == Ok(true))
|
|
649
650
|
})
|
|
650
651
|
};
|
|
651
|
-
normalize_host_grants(&mut grants, |grant| {
|
|
652
|
+
normalize_host_grants(&mut grants, "hostGrants", |grant| {
|
|
652
653
|
let endpoint = workload(&grant.destination_address).ok_or(Error::Invalid)?;
|
|
653
654
|
require(
|
|
654
655
|
endpoint.interface_kind == "veth"
|
|
@@ -695,7 +696,7 @@ fn normalize_router_workload_grants(value: &mut RouterScope) -> Result<()> {
|
|
|
695
696
|
require(endpoint.interface_kind == "veth" && !local && !platform)?;
|
|
696
697
|
Ok(endpoint)
|
|
697
698
|
};
|
|
698
|
-
normalize_host_grants(&mut grants, |grant| {
|
|
699
|
+
normalize_host_grants(&mut grants, "workloadGrants", |grant| {
|
|
699
700
|
let source = workload(&grant.source_address)?;
|
|
700
701
|
let destination = workload(&grant.destination_address)?;
|
|
701
702
|
require(source.id != destination.id)
|
|
@@ -711,8 +712,11 @@ pub const ROUTER_RULES: usize = 1024;
|
|
|
711
712
|
pub const ROUTER_GRANTS: usize = 1024;
|
|
712
713
|
/// Publications per scope; they are set elements on both hops.
|
|
713
714
|
pub const PUBLISHED_PORTS: usize = 1024;
|
|
715
|
+
/// Host grants per scope and workload grants per router, each a set element.
|
|
716
|
+
pub const HOST_GRANTS: usize = 1024;
|
|
714
717
|
fn normalize_router(value: &mut RouterScope, revision: u64) -> Result<()> {
|
|
715
|
-
|
|
718
|
+
crate::capacity("links", ROUTER_LINKS, value.links.len())?;
|
|
719
|
+
require(value.generations.len() <= 32)?;
|
|
716
720
|
let private = policy::Policy {
|
|
717
721
|
schema_version: 1,
|
|
718
722
|
revision,
|
|
@@ -779,7 +783,11 @@ fn normalize_router(value: &mut RouterScope, revision: u64) -> Result<()> {
|
|
|
779
783
|
let mut zones = BTreeSet::new();
|
|
780
784
|
let mut labels = BTreeSet::new();
|
|
781
785
|
let mut classifiers: Vec<(&Grant, String)> = Vec::new();
|
|
782
|
-
|
|
786
|
+
crate::capacity(
|
|
787
|
+
"grants",
|
|
788
|
+
ROUTER_GRANTS,
|
|
789
|
+
value.generations.iter().map(|item| item.grants.len()).sum(),
|
|
790
|
+
)?;
|
|
783
791
|
value
|
|
784
792
|
.generations
|
|
785
793
|
.sort_by(|a, b| (&a.lease.id, a.lease.generation).cmp(&(&b.lease.id, b.lease.generation)));
|
|
@@ -805,8 +813,6 @@ fn normalize_router(value: &mut RouterScope, revision: u64) -> Result<()> {
|
|
|
805
813
|
&& generation.conntrack_label.bytes().any(|item| item != b'0')
|
|
806
814
|
&& labels.insert(generation.conntrack_label.clone()),
|
|
807
815
|
)?;
|
|
808
|
-
count += generation.grants.len();
|
|
809
|
-
require(count <= ROUTER_GRANTS)?;
|
|
810
816
|
generation.grants.sort();
|
|
811
817
|
for grant in &generation.grants {
|
|
812
818
|
require(
|
|
@@ -903,7 +909,7 @@ fn normalize_pool_guard(value: &mut AllocationPoolGuardScope) -> Result<()> {
|
|
|
903
909
|
}
|
|
904
910
|
// An exception is only ever an exact flow into a guarded pool.
|
|
905
911
|
let mut grants = std::mem::take(&mut value.host_grants);
|
|
906
|
-
normalize_host_grants(&mut grants, |grant| {
|
|
912
|
+
normalize_host_grants(&mut grants, "hostGrants", |grant| {
|
|
907
913
|
require(covers_address(&value.prefixes, &grant.destination_address)?)
|
|
908
914
|
})?;
|
|
909
915
|
value.host_grants = grants;
|
|
@@ -913,7 +919,7 @@ fn normalize_pool_guard(value: &mut AllocationPoolGuardScope) -> Result<()> {
|
|
|
913
919
|
/// wildcard or range, each with exactly one owning uid. `(uid_t)-1` is not a
|
|
914
920
|
/// user; it is the kernel's "unchanged" sentinel.
|
|
915
921
|
fn normalize_local_tcp_port_owners(owners: &mut [LocalTcpPortOwner]) -> Result<()> {
|
|
916
|
-
|
|
922
|
+
crate::capacity("localTcpPortOwners", LOCAL_TCP_PORT_OWNERS, owners.len())?;
|
|
917
923
|
owners.sort();
|
|
918
924
|
for (index, owner) in owners.iter().enumerate() {
|
|
919
925
|
let ip = address(&owner.address)?;
|
|
@@ -939,7 +945,7 @@ const LOCAL_TCP_PORT_OWNERS: usize = 8;
|
|
|
939
945
|
fn normalize_host_host_grants(value: &mut HostScope) -> Result<()> {
|
|
940
946
|
let mut grants = std::mem::take(&mut value.host_grants);
|
|
941
947
|
let scope = &*value;
|
|
942
|
-
normalize_host_grants(&mut grants, |grant| {
|
|
948
|
+
normalize_host_grants(&mut grants, "hostGrants", |grant| {
|
|
943
949
|
let destination = &grant.destination_address;
|
|
944
950
|
require(
|
|
945
951
|
scope
|
|
@@ -984,7 +990,7 @@ fn normalize_host_host_grants(value: &mut HostScope) -> Result<()> {
|
|
|
984
990
|
/// overlap with the leased outbound source ports translated to that same address.
|
|
985
991
|
fn normalize_published(value: &mut HostScope) -> Result<()> {
|
|
986
992
|
let mut ports = std::mem::take(&mut value.published_ports);
|
|
987
|
-
|
|
993
|
+
crate::capacity("publishedPorts", PUBLISHED_PORTS, ports.len())?;
|
|
988
994
|
ports.sort();
|
|
989
995
|
for (index, port) in ports.iter().enumerate() {
|
|
990
996
|
require(
|
|
@@ -1051,8 +1051,15 @@ fn host_grants_meet_the_atomic_reserve_at_the_contract_maximum() {
|
|
|
1051
1051
|
program.len() < 768 && graph <= super::compile::RULE_BYTES && graph + elements <= super::compile::TARGET_BYTES
|
|
1052
1052
|
);
|
|
1053
1053
|
assert!(deletion + graph + elements + 64 <= crate::wire::BATCH_BYTES);
|
|
1054
|
-
// The contract maximum is
|
|
1055
|
-
|
|
1054
|
+
// The contract maximum is a capacity bound.
|
|
1055
|
+
assert_eq!(
|
|
1056
|
+
normalized(granted(fixture, many(1025))).unwrap_err(),
|
|
1057
|
+
crate::Error::Exhausted(crate::Bound {
|
|
1058
|
+
name: "hostGrants",
|
|
1059
|
+
limit: 1024,
|
|
1060
|
+
actual: 1025
|
|
1061
|
+
})
|
|
1062
|
+
);
|
|
1056
1063
|
println!(
|
|
1057
1064
|
"HOST_GRANT_MAX scope={name} grants=1024 operations={} rule_bytes={graph} element_bytes={elements} batch_bound={}",
|
|
1058
1065
|
program.len(),
|
|
@@ -262,5 +262,12 @@ fn local_tcp_port_owners_meet_the_atomic_reserve_at_the_contract_maximum() {
|
|
|
262
262
|
.as_array_mut()
|
|
263
263
|
.unwrap()
|
|
264
264
|
.push(owner("127.0.0.1", 10008, 0));
|
|
265
|
-
|
|
265
|
+
assert_eq!(
|
|
266
|
+
normalized(value).unwrap_err(),
|
|
267
|
+
crate::Error::Exhausted(crate::Bound {
|
|
268
|
+
name: "localTcpPortOwners",
|
|
269
|
+
limit: 8,
|
|
270
|
+
actual: 9
|
|
271
|
+
})
|
|
272
|
+
);
|
|
266
273
|
}
|
package/rust/src/egress_tests.rs
CHANGED
|
@@ -992,3 +992,160 @@ fn router_v2_published_endpoint_traffic_is_classified_before_the_leased_grants()
|
|
|
992
992
|
);
|
|
993
993
|
}
|
|
994
994
|
}
|
|
995
|
+
|
|
996
|
+
fn exhausted(value: super::Result<Prepared>) -> crate::Bound {
|
|
997
|
+
match value {
|
|
998
|
+
Err(crate::Error::Exhausted(bound)) => bound,
|
|
999
|
+
other => panic!("expected a capacity refusal, got {other:?}"),
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
fn bound(name: &'static str, limit: usize, actual: usize) -> crate::Bound {
|
|
1003
|
+
crate::Bound {
|
|
1004
|
+
name,
|
|
1005
|
+
limit,
|
|
1006
|
+
actual,
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
fn prepare(value: Value) -> super::Result<Prepared> {
|
|
1010
|
+
normalized(value)?.prepare()
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
/// The compiled budget and the input counts that exist to hold it refuse with
|
|
1014
|
+
/// EXHAUSTED and their bound; bounds of the topology's shape stay INVALID.
|
|
1015
|
+
#[test]
|
|
1016
|
+
fn v2_capacity_refusals_name_their_bound_and_shape_bounds_stay_invalid() {
|
|
1017
|
+
// Router rules grow with protected prefixes: 128 exceed the rule reserve.
|
|
1018
|
+
let mut value = router();
|
|
1019
|
+
let mut prefixes = vec![
|
|
1020
|
+
json!("10.0.0.0/8"),
|
|
1021
|
+
json!("192.0.2.0/24"),
|
|
1022
|
+
json!("203.0.113.10/32"),
|
|
1023
|
+
];
|
|
1024
|
+
prefixes.extend((0..125).map(|index| json!(format!("172.16.{index}.0/24"))));
|
|
1025
|
+
value["scope"]["protection"]["prefixes"] = json!(prefixes);
|
|
1026
|
+
let refused = exhausted(prepare(value.clone()));
|
|
1027
|
+
assert_eq!((refused.name, refused.limit), ("ruleBytes", 100_000));
|
|
1028
|
+
assert!(refused.actual > refused.limit);
|
|
1029
|
+
// 129 protected prefixes are outside the schema, not over capacity.
|
|
1030
|
+
value["scope"]["protection"]["prefixes"]
|
|
1031
|
+
.as_array_mut()
|
|
1032
|
+
.unwrap()
|
|
1033
|
+
.push(json!("172.17.0.0/24"));
|
|
1034
|
+
assert_eq!(prepare(value).unwrap_err(), crate::Error::Invalid);
|
|
1035
|
+
|
|
1036
|
+
// Host transit rules grow with its handoffs and their leased ranges.
|
|
1037
|
+
let handoff = |index: u32, ranges: u32| {
|
|
1038
|
+
let mut item = allocation();
|
|
1039
|
+
item["lease"]["id"] = json!(format!("lease-{index}"));
|
|
1040
|
+
item["transitSourceAddress"] = json!(format!("10.240.{index}.2"));
|
|
1041
|
+
item["sourcePortRanges"] = json!((0..ranges)
|
|
1042
|
+
.map(
|
|
1043
|
+
|range| json!({"protocol":if range % 2 == 0 { "tcp" } else { "udp" },
|
|
1044
|
+
"first":10000 + range * 100,"last":10099 + range * 100})
|
|
1045
|
+
)
|
|
1046
|
+
.collect::<Vec<_>>());
|
|
1047
|
+
json!({"link":link(10 + index, &format!("h{index}"), "veth", &format!("10.240.{index}.1")),
|
|
1048
|
+
"allocations":[item]})
|
|
1049
|
+
};
|
|
1050
|
+
let handoffs = |count: u32, ranges: u32| {
|
|
1051
|
+
json!((0..count)
|
|
1052
|
+
.map(|index| handoff(index, ranges))
|
|
1053
|
+
.collect::<Vec<_>>())
|
|
1054
|
+
};
|
|
1055
|
+
let mut value = host();
|
|
1056
|
+
value["scope"]["handoffs"] = handoffs(4, 2);
|
|
1057
|
+
assert_eq!(prepare(value.clone()).map(|_| ()), Ok(()));
|
|
1058
|
+
value["scope"]["handoffs"] = handoffs(32, 16);
|
|
1059
|
+
let refused = exhausted(prepare(value.clone()));
|
|
1060
|
+
assert_eq!((refused.name, refused.limit), ("ruleBytes", 100_000));
|
|
1061
|
+
assert!(refused.actual > refused.limit);
|
|
1062
|
+
// 33 handoffs are outside the schema, not over capacity.
|
|
1063
|
+
value["scope"]["handoffs"] = handoffs(33, 2);
|
|
1064
|
+
assert_eq!(prepare(value).unwrap_err(), crate::Error::Invalid);
|
|
1065
|
+
|
|
1066
|
+
// Router input counts.
|
|
1067
|
+
let mut value = router();
|
|
1068
|
+
let template = value["scope"]["links"][0].clone();
|
|
1069
|
+
value["scope"]["links"] = json!((0..129u32)
|
|
1070
|
+
.map(|index| {
|
|
1071
|
+
let mut item = template.clone();
|
|
1072
|
+
item["interfaceIndex"] = json!(10 + index);
|
|
1073
|
+
item["interfaceName"] = json!(format!("l{index}"));
|
|
1074
|
+
item
|
|
1075
|
+
})
|
|
1076
|
+
.collect::<Vec<_>>());
|
|
1077
|
+
assert_eq!(exhausted(prepare(value)), bound("links", 128, 129));
|
|
1078
|
+
let mut value = router();
|
|
1079
|
+
let template = value["scope"]["endpoints"][0].clone();
|
|
1080
|
+
value["scope"]["endpoints"] = json!((0..129u32)
|
|
1081
|
+
.map(|index| {
|
|
1082
|
+
let mut item = template.clone();
|
|
1083
|
+
item["id"] = json!(format!("e{index}"));
|
|
1084
|
+
item
|
|
1085
|
+
})
|
|
1086
|
+
.collect::<Vec<_>>());
|
|
1087
|
+
assert_eq!(exhausted(prepare(value)), bound("endpoints", 128, 129));
|
|
1088
|
+
let mut value = router();
|
|
1089
|
+
let template = value["scope"]["rules"][0].clone();
|
|
1090
|
+
value["scope"]["rules"] = json!((0..1025u16)
|
|
1091
|
+
.map(|index| {
|
|
1092
|
+
let mut item = template.clone();
|
|
1093
|
+
item["destinationPort"] = json!(1 + index);
|
|
1094
|
+
item
|
|
1095
|
+
})
|
|
1096
|
+
.collect::<Vec<_>>());
|
|
1097
|
+
assert_eq!(exhausted(prepare(value)), bound("rules", 1024, 1025));
|
|
1098
|
+
// Egress grants count across every generation.
|
|
1099
|
+
let mut value = router();
|
|
1100
|
+
let generation = value["scope"]["generations"][0].clone();
|
|
1101
|
+
let template = generation["grants"][0].clone();
|
|
1102
|
+
value["scope"]["generations"] = json!((0..2u16)
|
|
1103
|
+
.map(|part| {
|
|
1104
|
+
let mut item = generation.clone();
|
|
1105
|
+
item["grants"] = json!((0..(512 + part))
|
|
1106
|
+
.map(|index| {
|
|
1107
|
+
let mut grant = template.clone();
|
|
1108
|
+
grant["destinationPort"] = json!(1 + index);
|
|
1109
|
+
grant
|
|
1110
|
+
})
|
|
1111
|
+
.collect::<Vec<_>>());
|
|
1112
|
+
item
|
|
1113
|
+
})
|
|
1114
|
+
.collect::<Vec<_>>());
|
|
1115
|
+
assert_eq!(exhausted(prepare(value)), bound("grants", 1024, 1025));
|
|
1116
|
+
// Active generations are lease lifecycle, not capacity.
|
|
1117
|
+
let mut value = router();
|
|
1118
|
+
let generation = value["scope"]["generations"][0].clone();
|
|
1119
|
+
value["scope"]["generations"] = json!(vec![generation; 33]);
|
|
1120
|
+
assert_eq!(prepare(value).unwrap_err(), crate::Error::Invalid);
|
|
1121
|
+
|
|
1122
|
+
// Publications on both hops.
|
|
1123
|
+
let ports = json!((0..1025u16)
|
|
1124
|
+
.map(|index| published_port("tcp", 20000 + index, 20000 + index))
|
|
1125
|
+
.collect::<Vec<_>>());
|
|
1126
|
+
assert_eq!(
|
|
1127
|
+
exhausted(prepare(published_host(ports.clone()))),
|
|
1128
|
+
bound("publishedPorts", 1024, 1025)
|
|
1129
|
+
);
|
|
1130
|
+
let ports = json!((0..1025u16)
|
|
1131
|
+
.map(|index| router_published_port("tcp", 20000 + index, 20000 + index))
|
|
1132
|
+
.collect::<Vec<_>>());
|
|
1133
|
+
assert_eq!(
|
|
1134
|
+
exhausted(prepare(published_router(ports))),
|
|
1135
|
+
bound("publishedPorts", 1024, 1025)
|
|
1136
|
+
);
|
|
1137
|
+
|
|
1138
|
+
// Within the input bound, the grant elements exceed the target budget.
|
|
1139
|
+
let mut value = router();
|
|
1140
|
+
let template = value["scope"]["generations"][0]["grants"][0].clone();
|
|
1141
|
+
value["scope"]["generations"][0]["grants"] = json!((0..1024u16)
|
|
1142
|
+
.map(|index| {
|
|
1143
|
+
let mut grant = template.clone();
|
|
1144
|
+
grant["destinationPort"] = json!(1000 + index);
|
|
1145
|
+
grant
|
|
1146
|
+
})
|
|
1147
|
+
.collect::<Vec<_>>());
|
|
1148
|
+
let refused = exhausted(prepare(value));
|
|
1149
|
+
assert_eq!((refused.name, refused.limit), ("targetBytes", 212_000));
|
|
1150
|
+
assert!(refused.actual > refused.limit);
|
|
1151
|
+
}
|
|
@@ -241,12 +241,6 @@ fn workload_grants_reject_unbound_same_link_local_platform_and_tun_ends() {
|
|
|
241
241
|
json!([{"protocol":"tcp","sourceAddress":"10.241.0.2","destinationAddress":"10.241.1.2",
|
|
242
242
|
"destinationPort":80,"bidirectional":true}]),
|
|
243
243
|
),
|
|
244
|
-
granted(
|
|
245
|
-
two_workloads(),
|
|
246
|
-
json!((0..1025_u16)
|
|
247
|
-
.map(|port| workload_grant("tcp", "10.241.0.2", "10.241.1.2", 1 + port))
|
|
248
|
-
.collect::<Vec<_>>()),
|
|
249
|
-
),
|
|
250
244
|
] {
|
|
251
245
|
assert!(
|
|
252
246
|
normalized(invalid.clone()).is_err(),
|
|
@@ -254,6 +248,21 @@ fn workload_grants_reject_unbound_same_link_local_platform_and_tun_ends() {
|
|
|
254
248
|
invalid["scope"]["workloadGrants"]
|
|
255
249
|
);
|
|
256
250
|
}
|
|
251
|
+
// The contract maximum is a capacity bound.
|
|
252
|
+
assert_eq!(
|
|
253
|
+
normalized(granted(
|
|
254
|
+
two_workloads(),
|
|
255
|
+
json!((0..1025_u16)
|
|
256
|
+
.map(|port| workload_grant("tcp", "10.241.0.2", "10.241.1.2", 1 + port))
|
|
257
|
+
.collect::<Vec<_>>()),
|
|
258
|
+
))
|
|
259
|
+
.unwrap_err(),
|
|
260
|
+
crate::Error::Exhausted(crate::Bound {
|
|
261
|
+
name: "workloadGrants",
|
|
262
|
+
limit: 1024,
|
|
263
|
+
actual: 1025
|
|
264
|
+
})
|
|
265
|
+
);
|
|
257
266
|
// Both directions between two workloads are two separate grants.
|
|
258
267
|
assert!(normalized(granted(
|
|
259
268
|
two_workloads(),
|
package/rust/src/main.rs
CHANGED
|
@@ -15,9 +15,22 @@ use std::io::{BufRead, Read, Write};
|
|
|
15
15
|
/// same lines. A transition carries two complete policies.
|
|
16
16
|
pub(crate) const IPC_BYTES: usize = 1_048_576;
|
|
17
17
|
|
|
18
|
+
/// A capacity bound of the atomic replacement budget that a policy exceeded: the
|
|
19
|
+
/// stable bound name, its limit and the size the policy reached. Input counts and
|
|
20
|
+
/// the schema-v1 budget are exact; the other compiled budgets stop at the first
|
|
21
|
+
/// message or rule past the limit, so the complete policy needs at least `actual`.
|
|
22
|
+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
23
|
+
pub struct Bound {
|
|
24
|
+
pub name: &'static str,
|
|
25
|
+
pub limit: usize,
|
|
26
|
+
pub actual: usize,
|
|
27
|
+
}
|
|
28
|
+
|
|
18
29
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
19
30
|
pub enum Error {
|
|
20
31
|
Invalid,
|
|
32
|
+
/// The policy does not fit a capacity bound; a smaller policy may fit.
|
|
33
|
+
Exhausted(Bound),
|
|
21
34
|
Unsupported,
|
|
22
35
|
UnsupportedBackend,
|
|
23
36
|
Permission,
|
|
@@ -28,10 +41,23 @@ pub enum Error {
|
|
|
28
41
|
Unavailable,
|
|
29
42
|
}
|
|
30
43
|
pub type Result<T> = std::result::Result<T, Error>;
|
|
44
|
+
/// Refuses `actual` above the capacity bound `name`.
|
|
45
|
+
pub fn capacity(name: &'static str, limit: usize, actual: usize) -> Result<()> {
|
|
46
|
+
if actual > limit {
|
|
47
|
+
Err(Error::Exhausted(Bound {
|
|
48
|
+
name,
|
|
49
|
+
limit,
|
|
50
|
+
actual,
|
|
51
|
+
}))
|
|
52
|
+
} else {
|
|
53
|
+
Ok(())
|
|
54
|
+
}
|
|
55
|
+
}
|
|
31
56
|
impl Error {
|
|
32
57
|
pub fn code(self) -> &'static str {
|
|
33
58
|
match self {
|
|
34
59
|
Self::Invalid => "INVALID",
|
|
60
|
+
Self::Exhausted(_) => "EXHAUSTED",
|
|
35
61
|
Self::Unsupported => "UNSUPPORTED_KERNEL",
|
|
36
62
|
Self::UnsupportedBackend => "UNSUPPORTED_BACKEND",
|
|
37
63
|
Self::Permission => "PERMISSION",
|
|
@@ -42,6 +68,26 @@ impl Error {
|
|
|
42
68
|
Self::Unavailable => "UNAVAILABLE",
|
|
43
69
|
}
|
|
44
70
|
}
|
|
71
|
+
/// The bounded, non-secret refusal text of the response.
|
|
72
|
+
pub fn message(self) -> String {
|
|
73
|
+
match self {
|
|
74
|
+
Self::Invalid => "Managed policy input is invalid.".into(),
|
|
75
|
+
Self::Exhausted(bound) => format!(
|
|
76
|
+
"Managed policy exceeds the {} capacity of {}: it reached {}.",
|
|
77
|
+
bound.name, bound.limit, bound.actual
|
|
78
|
+
),
|
|
79
|
+
_ => "Managed policy operation failed.".into(),
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/// The structured refusal details of the response, if any.
|
|
83
|
+
pub fn data(self) -> Option<serde_json::Value> {
|
|
84
|
+
match self {
|
|
85
|
+
Self::Exhausted(bound) => Some(
|
|
86
|
+
serde_json::json!({"bound":bound.name,"limit":bound.limit,"actual":bound.actual}),
|
|
87
|
+
),
|
|
88
|
+
_ => None,
|
|
89
|
+
}
|
|
90
|
+
}
|
|
45
91
|
pub fn kernel(code: i32) -> Self {
|
|
46
92
|
match -code {
|
|
47
93
|
libc::ENOENT => Self::Absent,
|
|
@@ -114,6 +160,16 @@ fn dispatch(owner: &mut Option<owner::Owner>, request: Request) -> Result<serde_
|
|
|
114
160
|
}
|
|
115
161
|
}
|
|
116
162
|
|
|
163
|
+
/// The failure response line: the refusal text, the stable code and, for a
|
|
164
|
+
/// capacity refusal, its bound.
|
|
165
|
+
fn failure(id: &str, error: Error) -> serde_json::Value {
|
|
166
|
+
let mut value = serde_json::json!({"id":id,"success":false,"error":error.message(),"errorCode":error.code()});
|
|
167
|
+
if let Some(data) = error.data() {
|
|
168
|
+
value["errorData"] = data;
|
|
169
|
+
}
|
|
170
|
+
value
|
|
171
|
+
}
|
|
172
|
+
|
|
117
173
|
fn main() {
|
|
118
174
|
let arguments = std::env::args().skip(1).collect::<Vec<_>>();
|
|
119
175
|
let (docker_mode, descriptor) = match arguments.as_slice() {
|
|
@@ -163,9 +219,7 @@ fn main() {
|
|
|
163
219
|
let id = request.id.clone();
|
|
164
220
|
let response = match if docker_mode { docker::dispatch(&mut docker, request) } else { dispatch(&mut owner, request) } {
|
|
165
221
|
Ok(result) => serde_json::json!({"id":id,"success":true,"result":result}),
|
|
166
|
-
Err(error) =>
|
|
167
|
-
serde_json::json!({"id":id,"success":false,"error":"Managed policy operation failed.","errorCode":error.code()})
|
|
168
|
-
}
|
|
222
|
+
Err(error) => failure(&id, error),
|
|
169
223
|
};
|
|
170
224
|
if writeln!(output, "{response}")
|
|
171
225
|
.and_then(|_| output.flush())
|
package/rust/src/policy.rs
CHANGED
|
@@ -101,29 +101,26 @@ impl Policy {
|
|
|
101
101
|
let program = prepared.program(&format!("snft_{}", "x".repeat(59)))?;
|
|
102
102
|
// Reserve enough of the batch for deleting a previous maximum
|
|
103
103
|
// graph, then installing this complete graph in the same transaction.
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
crate::capacity("operations", 768, program.len())?;
|
|
105
|
+
crate::capacity(
|
|
106
|
+
"ruleBytes",
|
|
107
|
+
100_000,
|
|
108
|
+
program
|
|
106
109
|
.iter()
|
|
107
110
|
.map(|(_, attrs)| wire::encode_attrs(attrs).len() + 20)
|
|
108
|
-
.sum
|
|
109
|
-
|
|
110
|
-
{
|
|
111
|
-
return Err(Error::Invalid);
|
|
112
|
-
}
|
|
111
|
+
.sum(),
|
|
112
|
+
)?;
|
|
113
113
|
Ok(prepared)
|
|
114
114
|
}
|
|
115
115
|
/// Validates and orders the endpoints and rules. Schema v1 compiles one rule
|
|
116
116
|
/// per endpoint and grant, so it admits 32 endpoints and 128 rules; the
|
|
117
117
|
/// set-backed router scope of schema v2 admits more.
|
|
118
118
|
pub(crate) fn normalize(mut self, endpoints: usize, rules: usize) -> Result<Self> {
|
|
119
|
-
if self.schema_version != 1
|
|
120
|
-
|| self.revision == 0
|
|
121
|
-
|| self.revision > 9_007_199_254_740_991
|
|
122
|
-
|| self.endpoints.len() > endpoints
|
|
123
|
-
|| self.rules.len() > rules
|
|
124
|
-
{
|
|
119
|
+
if self.schema_version != 1 || self.revision == 0 || self.revision > 9_007_199_254_740_991 {
|
|
125
120
|
return Err(Error::Invalid);
|
|
126
121
|
}
|
|
122
|
+
crate::capacity("endpoints", endpoints, self.endpoints.len())?;
|
|
123
|
+
crate::capacity("rules", rules, self.rules.len())?;
|
|
127
124
|
let mut ids = BTreeSet::new();
|
|
128
125
|
let mut indices = BTreeSet::new();
|
|
129
126
|
let mut names = BTreeSet::new();
|
package/rust/src/tests.rs
CHANGED
|
@@ -117,3 +117,103 @@ fn compiler_bounds_full_policy_and_keeps_directed_port_grants() {
|
|
|
117
117
|
value.revision = 9_007_199_254_740_992;
|
|
118
118
|
assert_eq!(value.prepare(), Err(Error::Invalid));
|
|
119
119
|
}
|
|
120
|
+
|
|
121
|
+
fn exhausted<T>(name: &'static str, limit: usize, actual: usize) -> crate::Result<T> {
|
|
122
|
+
Err(Error::Exhausted(crate::Bound {
|
|
123
|
+
name,
|
|
124
|
+
limit,
|
|
125
|
+
actual,
|
|
126
|
+
}))
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/// Every endpoint with 16 prefixes and `rules` distinct directed rules between
|
|
130
|
+
/// them: well-formed input whose only fault can be its size.
|
|
131
|
+
fn sized(endpoints: u32, rules: u16) -> Policy {
|
|
132
|
+
let mut value = policy();
|
|
133
|
+
let template = value.endpoints[0].clone();
|
|
134
|
+
value.endpoints = (0..endpoints)
|
|
135
|
+
.map(|index| {
|
|
136
|
+
let mut endpoint = template.clone();
|
|
137
|
+
endpoint.id = format!("e{index}");
|
|
138
|
+
endpoint.interface_index = 10 + index;
|
|
139
|
+
endpoint.interface_name = format!("w{index}");
|
|
140
|
+
endpoint.source_prefixes = (0..16)
|
|
141
|
+
.map(|part| format!("10.{}.{part}.0/24", 100 + index))
|
|
142
|
+
.collect();
|
|
143
|
+
endpoint
|
|
144
|
+
})
|
|
145
|
+
.collect();
|
|
146
|
+
let template = value.rules[0].clone();
|
|
147
|
+
value.rules = (0..rules)
|
|
148
|
+
.map(|index| {
|
|
149
|
+
let (from, to) = (
|
|
150
|
+
u32::from(index) % endpoints,
|
|
151
|
+
(u32::from(index) + 1) % endpoints,
|
|
152
|
+
);
|
|
153
|
+
let mut rule = template.clone();
|
|
154
|
+
rule.source_endpoint = Some(format!("e{from}"));
|
|
155
|
+
rule.destination_endpoint = Some(format!("e{to}"));
|
|
156
|
+
rule.source_prefix = format!("10.{}.0.0/24", 100 + from);
|
|
157
|
+
rule.destination_prefix = format!("10.{}.0.0/24", 100 + to);
|
|
158
|
+
rule.destination_port = Some(1000 + index);
|
|
159
|
+
rule
|
|
160
|
+
})
|
|
161
|
+
.collect();
|
|
162
|
+
value
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
#[test]
|
|
166
|
+
fn v1_capacity_refusals_name_their_bound_and_invalid_input_stays_invalid() {
|
|
167
|
+
assert_eq!(sized(2, 2).prepare().map(|_| ()), Ok(()));
|
|
168
|
+
// Within the schema's counts, the complete graph exceeds the byte and the
|
|
169
|
+
// operation budget; the complete program is measured, so both are exact.
|
|
170
|
+
assert_eq!(
|
|
171
|
+
sized(8, 16).prepare(),
|
|
172
|
+
exhausted("ruleBytes", 100_000, 186_952)
|
|
173
|
+
);
|
|
174
|
+
assert_eq!(sized(32, 128).prepare(), exhausted("operations", 768, 1667));
|
|
175
|
+
assert_eq!(sized(33, 16).prepare(), exhausted("endpoints", 32, 33));
|
|
176
|
+
assert_eq!(sized(8, 129).prepare(), exhausted("rules", 128, 129));
|
|
177
|
+
// Invalid input is never reported as capacity, whatever its size.
|
|
178
|
+
let mut value = sized(33, 129);
|
|
179
|
+
value.schema_version = 2;
|
|
180
|
+
assert_eq!(value.prepare(), Err(Error::Invalid));
|
|
181
|
+
let mut value = sized(2, 2);
|
|
182
|
+
value.endpoints[0].interface_name = "w".repeat(16);
|
|
183
|
+
assert_eq!(value.prepare(), Err(Error::Invalid));
|
|
184
|
+
let mut value = sized(2, 2);
|
|
185
|
+
value.endpoints[0]
|
|
186
|
+
.source_prefixes
|
|
187
|
+
.push("10.200.0.0/24".into());
|
|
188
|
+
assert_eq!(value.prepare(), Err(Error::Invalid));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
#[test]
|
|
192
|
+
fn failure_responses_carry_the_refusal_reason_and_capacity_bound() {
|
|
193
|
+
let bound = crate::Bound {
|
|
194
|
+
name: "targetBytes",
|
|
195
|
+
limit: 212_000,
|
|
196
|
+
actual: 212_345,
|
|
197
|
+
};
|
|
198
|
+
assert_eq!(
|
|
199
|
+
crate::failure("7", Error::Exhausted(bound)),
|
|
200
|
+
serde_json::json!({"id":"7","success":false,
|
|
201
|
+
"error":"Managed policy exceeds the targetBytes capacity of 212000: it reached 212345.",
|
|
202
|
+
"errorCode":"EXHAUSTED","errorData":{"bound":"targetBytes","limit":212_000,"actual":212_345}})
|
|
203
|
+
);
|
|
204
|
+
assert_eq!(
|
|
205
|
+
crate::failure("8", Error::Invalid),
|
|
206
|
+
serde_json::json!({"id":"8","success":false,"error":"Managed policy input is invalid.","errorCode":"INVALID"})
|
|
207
|
+
);
|
|
208
|
+
for error in [
|
|
209
|
+
Error::Conflict,
|
|
210
|
+
Error::Permission,
|
|
211
|
+
Error::Protocol,
|
|
212
|
+
Error::Unavailable,
|
|
213
|
+
] {
|
|
214
|
+
assert_eq!(
|
|
215
|
+
crate::failure("9", error),
|
|
216
|
+
serde_json::json!({"id":"9","success":false,"error":"Managed policy operation failed.","errorCode":error.code()})
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
}
|
package/ts/00_commitinfo_data.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
|
-
import { snapshot, managedIpcBytes, ManagedNftablesError } from './classes.managednftables.js';
|
|
2
|
+
import { snapshot, managedIpcBytes, ManagedNftablesError, nativeError } from './classes.managednftables.js';
|
|
3
3
|
import type { IManagedDockerForwardingOptions, IManagedDockerForwardingStatus, IManagedDockerForwardingPolicy, IPreparedManagedDockerForwardingPolicy, IManagedDockerForwardingTransition, IAppliedManagedDockerForwardingPolicy, TManagedDockerForwardingCommands } from './managed.docker.types.js';
|
|
4
4
|
|
|
5
5
|
/** One node-level Docker DOCKER-USER contribution. Caller owns durable intents and handoff lifetimes. */
|
|
@@ -103,8 +103,7 @@ export class ManagedDockerForwarding {
|
|
|
103
103
|
#track<T>(work: Promise<T>): Promise<T> {
|
|
104
104
|
const owned = work.catch((error) => {
|
|
105
105
|
if (this.#state !== 'closing') this.#state = 'failed-owned';
|
|
106
|
-
|
|
107
|
-
throw new ManagedNftablesError(typeof code === 'string' && /^[A-Z_]{1,64}$/.test(code) ? code : 'UNAVAILABLE');
|
|
106
|
+
throw nativeError(error);
|
|
108
107
|
});
|
|
109
108
|
this.#work = owned;
|
|
110
109
|
void owned.then(() => { this.#work = null; }, () => { this.#work = null; });
|
|
@@ -1,9 +1,46 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
|
-
import type { IManagedNftOptions, IManagedNftPolicy, IManagedNftTransition, IAppliedManagedNftPolicy,
|
|
3
|
-
|
|
2
|
+
import type { IManagedNftOptions, IManagedNftPolicy, IManagedNftTransition, IAppliedManagedNftPolicy, IPreparedManagedNftPolicy,
|
|
3
|
+
IManagedNftStatus, IManagedNftTransitionRelease, TManagedNftCommands, TManagedNftPolicy, IManagedNftCapacity,
|
|
4
|
+
TManagedNftCapacityBound } from './managed.types.js';
|
|
4
5
|
|
|
6
|
+
/** `INVALID`: the input is outside the contract. `EXHAUSTED`: the policy exceeds a
|
|
7
|
+
* capacity bound of the atomic replacement budget, and a smaller policy may fit.
|
|
8
|
+
* Both carry the native refusal text in `reason`; `EXHAUSTED` also names its
|
|
9
|
+
* bound in `details`. Every other code, and the facade's own rejections, carry
|
|
10
|
+
* neither. */
|
|
5
11
|
export class ManagedNftablesError extends Error {
|
|
6
|
-
constructor(public readonly code: string
|
|
12
|
+
constructor(public readonly code: string, public readonly reason: string | null = null,
|
|
13
|
+
public readonly details: Readonly<IManagedNftCapacity> | null = null) {
|
|
14
|
+
super(reason === null ? `Managed nftables ${code}.` : `Managed nftables ${code}: ${reason}`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const capacityBounds: ReadonlySet<string> = new Set<TManagedNftCapacityBound>(['ruleBytes', 'targetBytes', 'operations',
|
|
19
|
+
'endpoints', 'links', 'rules', 'grants', 'hostGrants', 'workloadGrants', 'publishedPorts', 'localTcpPortOwners',
|
|
20
|
+
'restoreRules', 'restoreBytes']);
|
|
21
|
+
|
|
22
|
+
function capacity(value: unknown): Readonly<IManagedNftCapacity> | null {
|
|
23
|
+
if (typeof value !== 'object' || value === null || Object.getPrototypeOf(value) !== Object.prototype
|
|
24
|
+
|| Object.keys(value).sort().join(',') !== 'actual,bound,limit') return null;
|
|
25
|
+
const { bound, limit, actual } = value as Record<string, unknown>;
|
|
26
|
+
if (typeof bound !== 'string' || !capacityBounds.has(bound) || typeof limit !== 'number' || typeof actual !== 'number'
|
|
27
|
+
|| !Number.isSafeInteger(limit) || !Number.isSafeInteger(actual) || limit < 0 || actual <= limit) return null;
|
|
28
|
+
return Object.freeze({ bound: bound as TManagedNftCapacityBound, limit, actual });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Maps a failed native request. An `INVALID` or `EXHAUSTED` response keeps the
|
|
32
|
+
* bounded native refusal text and, for `EXHAUSTED`, its bound; a refusal outside
|
|
33
|
+
* that shape is `PROTOCOL`. Other responses keep only their code, and a failure
|
|
34
|
+
* without a native code is `UNAVAILABLE`. */
|
|
35
|
+
export function nativeError(error: unknown): ManagedNftablesError {
|
|
36
|
+
if (!(error instanceof plugins.smartrust.RustBridgeRequestError)) return new ManagedNftablesError('UNAVAILABLE');
|
|
37
|
+
const { responseErrorCode: code, message: reason, responseErrorData: data } = error;
|
|
38
|
+
if (typeof code !== 'string' || !/^[A-Z_]{1,64}$/.test(code)) return new ManagedNftablesError('UNAVAILABLE');
|
|
39
|
+
if (code !== 'INVALID' && code !== 'EXHAUSTED') return new ManagedNftablesError(code);
|
|
40
|
+
if (!/^[\x20-\x7e]{1,256}$/.test(reason)) return new ManagedNftablesError('PROTOCOL');
|
|
41
|
+
if (code === 'INVALID') return data === undefined ? new ManagedNftablesError(code, reason) : new ManagedNftablesError('PROTOCOL');
|
|
42
|
+
const details = capacity(data);
|
|
43
|
+
return details ? new ManagedNftablesError(code, reason, details) : new ManagedNftablesError('PROTOCOL');
|
|
7
44
|
}
|
|
8
45
|
|
|
9
46
|
/** Bounds of the private native IPC. A status carries up to three complete policies (applied,
|
|
@@ -240,16 +277,17 @@ export class ManagedNftables<TPolicy extends TManagedNftPolicy = IManagedNftPoli
|
|
|
240
277
|
#track<T>(work: Promise<T>, method: keyof TManagedNftCommands<TPolicy>): Promise<T> {
|
|
241
278
|
const owned = work.catch((error) => {
|
|
242
279
|
if (this.#state !== 'closing') this.#state = 'failed-owned';
|
|
243
|
-
const
|
|
244
|
-
// Only a native compiler rejection or explicit INVALID
|
|
245
|
-
// effect-free validation
|
|
280
|
+
const failure = nativeError(error);
|
|
281
|
+
// Only a native compiler rejection or explicit INVALID or EXHAUSTED
|
|
282
|
+
// response proves effect-free validation: the native owner refuses both
|
|
283
|
+
// before any kernel work. Other native errors can follow lost ownership
|
|
246
284
|
// or admitted kernel work. Recovery stays available; confidence is lost.
|
|
247
285
|
const validation = error instanceof plugins.smartrust.RustBridgeRequestError
|
|
248
286
|
&& error.code === 'ERR_RUST_BRIDGE_REQUEST_RUST_RESPONSE'
|
|
249
|
-
&& (method === 'preparePolicy' || code === 'INVALID');
|
|
287
|
+
&& (method === 'preparePolicy' || failure.code === 'INVALID' || failure.code === 'EXHAUSTED');
|
|
250
288
|
if (!validation && (this.#state !== 'closing'
|
|
251
289
|
|| ['reconcilePolicy', 'releasePolicy', 'releasePolicyTransition', 'detachPolicy'].includes(method))) this.#fail();
|
|
252
|
-
throw
|
|
290
|
+
throw failure;
|
|
253
291
|
});
|
|
254
292
|
this.#work = owned;
|
|
255
293
|
void owned.then(() => { this.#work = null; }, () => { this.#work = null; });
|
package/ts/managed.types.ts
CHANGED
|
@@ -93,6 +93,23 @@ export interface IManagedNftOptions {
|
|
|
93
93
|
|
|
94
94
|
export type TManagedNftPolicy = IManagedNftPolicy | IManagedNftPolicyV2;
|
|
95
95
|
|
|
96
|
+
/** Capacity bounds of the atomic replacement budget. The compiled target's
|
|
97
|
+
* `ruleBytes` (100,000), `targetBytes` (212,000) and `operations` (768), the input
|
|
98
|
+
* counts that exist to hold it (`endpoints`, `links`, `rules`, `grants`,
|
|
99
|
+
* `hostGrants`, `workloadGrants`, `publishedPorts`, `localTcpPortOwners`) and the
|
|
100
|
+
* Docker forwarding contribution's `restoreRules` (192) and `restoreBytes` (100,000). */
|
|
101
|
+
export type TManagedNftCapacityBound = 'ruleBytes' | 'targetBytes' | 'operations' | 'endpoints' | 'links' | 'rules'
|
|
102
|
+
| 'grants' | 'hostGrants' | 'workloadGrants' | 'publishedPorts' | 'localTcpPortOwners' | 'restoreRules' | 'restoreBytes';
|
|
103
|
+
|
|
104
|
+
/** The bound an `EXHAUSTED` refusal names. `actual` is always above `limit`. It is
|
|
105
|
+
* exact for input counts and the schema-v1 budget; the other compiled budgets stop
|
|
106
|
+
* at the first message or rule past the limit, so the policy needs at least `actual`. */
|
|
107
|
+
export interface IManagedNftCapacity {
|
|
108
|
+
bound: TManagedNftCapacityBound;
|
|
109
|
+
limit: number;
|
|
110
|
+
actual: number;
|
|
111
|
+
}
|
|
112
|
+
|
|
96
113
|
export type TManagedNftCommands<TPolicy extends TManagedNftPolicy = IManagedNftPolicy> = {
|
|
97
114
|
preparePolicy: { params: TPolicy; result: IPreparedManagedNftPolicy<TPolicy> };
|
|
98
115
|
openOwner: { params: Pick<IManagedNftOptions, 'ownerId' | 'instanceId' | 'tableName'>; result: IManagedNftStatus<TPolicy> };
|