Add --delete-network-full to fully tear down a network's live state

--delete-network only ever removed the config entry, leaving the
bridge/iptables/persistent-namespace state behind. Since
ensure_network_provisioned() treats "bridge exists" as "already fully
provisioned" and skips re-adding anything, a network recreated with
the same name after a plain --delete-network silently never got a
fresh MASQUERADE rule if the old one had been removed separately by
hand -- the bridge itself was still there the whole time.

teardown_network_state() (network_bridge.{h,cpp}) is the reverse of
ensure_network_provisioned(): for extern, removes the MASQUERADE
rule(s) then deletes the bridge; for intern, removes the whole
persistent namespace in one step (destroys the bridge inside it too,
no separate ip link del needed). Deliberately leaves the IPv4/IPv6
forwarding sysctls alone -- those are global host state shared across
every extern network, not per-network. Each step is best-effort
(teardown_step(), logging a warning not an error on failure) since a
step "failing" because that piece was already gone by hand is the
expected case this exists to handle, not a reason to abort --
delete_network_command() doesn't gate the config removal on any of
this succeeding, unlike delete_volume_command()'s own -full variant.

--delete-network-full wired into cli_args.{h,cpp} the same way
--delete-volume-full is.

Verified as root via the doas rule: an extern network's bridge and
MASQUERADE rule were both confirmed gone after --delete-network-full,
and recreating a network with the same name went through
provision_bridge() fresh instead of short-circuiting on a stale
bridge_exists() check -- fixing exactly the gap reported (a manually
removed MASQUERADE rule never came back on delete+recreate). An intern
network's persistent namespace was likewise confirmed fully removed
and recreatable without conflict.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv3s5jckJKzh6JkMoi2Akz
This commit is contained in:
2026-08-30 17:59:52 +00:00
parent 69a18924b4
commit 534136339f
7 changed files with 137 additions and 10 deletions
+19
View File
@@ -87,3 +87,22 @@ bool should_use_veth(const NetworkEntry& network);
// entry) -- there's no separate "reconcile" path; calling this again just
// recreates whatever's missing.
bool ensure_network_provisioned(const NetworkEntry& network);
// Tears down `network`'s live host-side state -- the reverse of
// ensure_network_provisioned(). For `extern`: removes the MASQUERADE
// rule(s) (`ip6tables` first, if `ipv6`, then `iptables` -- purely for
// tidiness, since iptables doesn't require the interface a rule references
// to still exist), then deletes the bridge itself. For `intern`: removes
// the whole persistent namespace (persistent_netns.h) in one step, which
// destroys everything inside it -- the bridge included -- with no separate
// `ip link del` needed. Deliberately does **not** touch the IPv4/IPv6
// forwarding sysctls `provision_bridge()` enables for `extern` -- those are
// global host state shared across every `extern` network, not per-network,
// so disabling them here could break others still relying on them.
// Best-effort, like every other host-global teardown in this project
// (`remove_session_cgroup()`, `remove_port_forward()`): logs a warning and
// keeps going past any individual step that fails, rather than stopping --
// a step failing because that piece was already removed by hand (see
// ensure_network_provisioned()'s own existence-check caveat above) is the
// common case this exists to handle gracefully, not an error to abort on.
void teardown_network_state(const NetworkEntry& network);