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:
@@ -182,10 +182,20 @@ Source layout (all under `src/`):
|
||||
it's already a pure deterministic function of the name — then the IPv6
|
||||
subnet — or `"(no ipv6)"` — appended unaligned as the trailing column,
|
||||
nothing follows it).
|
||||
`delete_network_command()` currently only removes the config entry, the
|
||||
same as `delete_volume_command()`'s default (non-`-full`) behavior — it
|
||||
does not tear down the network's live bridge/namespace/iptables state (no
|
||||
`--delete-network-full` analog exists yet).
|
||||
`delete_network_command()` takes a `delete_full` bool, same shape as
|
||||
`delete_volume_command()`'s own: `--delete-network` (`false`) only removes
|
||||
the config entry, leaving the network's live host-side state untouched;
|
||||
`--delete-network-full` (`true`) additionally calls
|
||||
`teardown_network_state()` (`network_bridge.h`, see below) first. Unlike
|
||||
`delete_volume_command()`'s `-full` variant (which bails out *before*
|
||||
touching the config if its single `remove_all()` call fails),
|
||||
`delete_network_command()` doesn't gate the config removal on
|
||||
`teardown_network_state()`'s success at all — that function is
|
||||
deliberately best-effort/non-fatal per-step (see its own doc comment), so
|
||||
a step "failing" because that piece was already gone by hand (exactly
|
||||
`ensure_network_provisioned()`'s own existence-check caveat, above) is
|
||||
expected, not a reason to leave a network the user explicitly asked to
|
||||
delete sitting in the config.
|
||||
`write_config_command()` implements `-w/--write-config`: unlike
|
||||
`create_volume_command()`/`delete_volume_command()`'s use of
|
||||
`write_config_file()` (which only ever persists `AppConfig` fields that are
|
||||
@@ -662,6 +672,35 @@ Source layout (all under `src/`):
|
||||
all came up correctly; a real `intern` network's bridge came up inside its
|
||||
own dedicated namespace with neither forwarding nor a NAT rule, confirming
|
||||
the structural (not merely policy) isolation the design calls for.
|
||||
`teardown_network_state()` is the reverse: `--delete-network-full`
|
||||
(`commands.cpp`'s `delete_network_command()`) calls it to tear down
|
||||
exactly what `ensure_network_provisioned()` stood up. For `extern`:
|
||||
removes the `ip6tables` MASQUERADE rule (if `ipv6`) then the `iptables`
|
||||
one, then deletes the bridge itself — via a new `.cpp`-local
|
||||
`teardown_step()`, the same shape as `run_admin_command()` but logging a
|
||||
*warning*, not an error, on failure, since a step failing because that
|
||||
piece was already gone by hand is the expected, common case this exists
|
||||
to handle (exactly `ensure_network_provisioned()`'s own existence-check
|
||||
caveat above — this is precisely how a manually-removed MASQUERADE rule
|
||||
can go from "won't come back on recreate" to "cleanly torn down and
|
||||
recreated" once `--delete-network-full` exists at all). 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 never touches the
|
||||
IPv4/IPv6 forwarding sysctls `provision_bridge()` enables for `extern` —
|
||||
those are global host state shared across every `extern` network, not
|
||||
per-network, so turning them off here could break others still relying
|
||||
on them. **Verified end-to-end on this dev machine (root, via the scoped
|
||||
`doas` rule)**: an `extern` network's bridge and MASQUERADE rule were
|
||||
both confirmed gone after `--delete-network-full` (`ip link show`
|
||||
reporting "Device does not exist"), and recreating a network with the
|
||||
*same name* afterward correctly went through `provision_bridge()` again
|
||||
from scratch (confirmed via the debug log) instead of short-circuiting
|
||||
on a stale `bridge_exists()` check — fixing exactly the gap a user
|
||||
reported (a manually-removed MASQUERADE rule never came back on
|
||||
`--delete-network` + recreate, since the old bridge was silently still
|
||||
there); an `intern` network's persistent namespace was similarly
|
||||
confirmed fully removed and recreatable without conflict.
|
||||
`probe_veth_support()` (added for the tap+relay fallback, see
|
||||
`docs/networking-design.md`'s addendum and `network_join.{h,cpp}` below):
|
||||
the real target device supports `tun`/`tap` but not `veth`
|
||||
|
||||
Reference in New Issue
Block a user