Files
jschoubben 94e617915c The home segment moves off 192.168.1.0/24
It is the commonest home LAN range there is, so on an ordinary workstation the
lab's private segment and the machine's own network are the same addresses. The
scenario routes an egress machine explicitly and marks the rest unreachable, so
nothing leaked — but that guard was carrying the whole weight of a collision
nobody chose, and a guard is a bad place for that.

10.99.1.0/24 is still RFC 1918, so the bed still models a home LAN behind an
access point. It is simply far from what this kind of machine already has:
192.168.1 is the LAN, 172.16-31 and 192.168.16-95 are container bridges, and
10.10/10.42/10.208 are a tunnel, the mesh overlay and the virtualisation daemon.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-11 00:00:19 +02:00

58 lines
2.6 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { parseScenario } from "../src/declaration/parse.ts";
import { planRouters, ttlSeconds } from "../src/lifecycle/router.ts";
test("segments sharing a gateway declaration share ONE router", () => {
// That is what a VLAN-capable router is, and two routers sharing an external address
// would not work anyway.
const scenario = parseScenario(`scenario: x
segments:
pub: { kind: public, cidr: [192.0.2.0/24] }
home: { kind: private, cidr: [10.99.1.0/24], gateway: { to: pub, address: [192.0.2.5], nat: [v4] } }
iot: { kind: private, cidr: [192.168.30.0/24], gateway: { to: pub, address: [192.0.2.5], nat: [v4] } }
machines: { a: { at: { segment: home, address: [10.99.1.9] } } }`);
const plans = planRouters(scenario, "inst");
assert.equal(plans.length, 1, "one gateway declaration, one router");
assert.deepEqual(plans[0]?.inside.sort(), ["home", "iot"]);
});
test("different external addresses mean different routers", () => {
const scenario = parseScenario(`scenario: x
segments:
pub: { kind: public, cidr: [192.0.2.0/24] }
home: { kind: private, cidr: [10.99.1.0/24], gateway: { to: pub, address: [192.0.2.5], nat: [v4] } }
other: { kind: private, cidr: [192.168.30.0/24], gateway: { to: pub, address: [192.0.2.6], nat: [v4] } }
machines: { a: { at: { segment: home, address: [10.99.1.9] } } }`);
assert.equal(planRouters(scenario, "inst").length, 2);
});
test("a scenario with no gateways needs no routers", () => {
const scenario = parseScenario(`scenario: x
segments: { net: { kind: public, cidr: [192.0.2.0/24] } }
machines: { a: { at: { segment: net, address: [192.0.2.1] } } }`);
assert.equal(planRouters(scenario, "inst").length, 0);
});
test("forwardable and nat carry through to the plan", () => {
const scenario = parseScenario(`scenario: x
segments:
pub: { kind: public, cidr: [192.0.2.0/24] }
cafe: { kind: private, cidr: [10.50.0.0/16], gateway: { to: pub, address: [192.0.2.9], nat: [v4], forwardable: false, mapping_ttl: 30s } }
machines: { a: { at: { segment: cafe, address: [10.50.0.9] } } }`);
const plan = planRouters(scenario, "inst")[0];
assert.equal(plan?.forwardable, false);
assert.deepEqual(plan?.nat, ["v4"]);
assert.equal(plan?.mappingTtl, "30s");
});
test("mapping ttl parses the forms a declaration uses", () => {
assert.equal(ttlSeconds("30s"), 30);
assert.equal(ttlSeconds("120s"), 120);
assert.equal(ttlSeconds("2m"), 120);
assert.equal(ttlSeconds("1h"), 3600);
assert.equal(ttlSeconds("90"), 90);
assert.equal(ttlSeconds(undefined), undefined);
assert.equal(ttlSeconds("soon"), undefined);
});