Add veth capability probe + --no-veth network flag

Prep work for a tap+userspace-relay fallback: the real target device
supports tun/tap but not veth (CONFIG_VETH stripped from its kernel),
so -n/--network joins there can't use the veth-pair mechanism as-is.

probe_veth_support() (network_bridge.{h,cpp}) detects kernel veth
support the same way bwrap.cpp's kernel_supports_namespace() probes
namespace types: fork, unshare(CLONE_NEWNET) into a throwaway
namespace, try `ip link add ... type veth ...` there. should_use_veth()
combines that with a new per-network NetworkEntry::veth policy flag
(default true, config_file.h), mirroring how namespace_policy_enabled()
already combines kernel capability with policy for --unshare-xxx.

--no-veth at network-creation time (-n <name> --extern|--intern
--no-veth) sets veth: false, forcing the not-yet-built tap+relay
fallback even on a veth-capable kernel like this dev machine -- lets
that path be exercised here without the actual veth-less hardware.

Verified as root via the doas rule: probe_veth_support() returns true
on this dev machine (a real veth pair is created successfully), and
--no-veth correctly persists veth: false while should_use_veth() still
returns false regardless of kernel support.

The fallback itself (network_tap_relay.{h,cpp}) isn't wired in yet --
a --no-veth network simply has no way to join a container until that
lands.

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 15:59:40 +00:00
parent a9cd3bf643
commit 08895297a9
9 changed files with 141 additions and 19 deletions
+21
View File
@@ -49,6 +49,27 @@ std::vector<std::string> wrap_for_network(const NetworkEntry& network, std::vect
// file's tool set depends on the network's own kind/ipv6 setting.
bool check_network_dependencies(const NetworkEntry& network);
// Probes whether the running kernel supports veth (CONFIG_VETH) at all,
// independent of any one network's own `veth` policy flag
// (config_file.h's NetworkEntry::veth) -- the two are separate gates, same
// split as bwrap.cpp's own kernel-capability-vs-policy design for
// --unshare-xxx. Forks a child that unshare(CLONE_NEWNET)s into a throwaway
// namespace and attempts `ip link add ... type veth peer name ...` there
// (via the existing run_process()) -- the whole namespace, and anything
// created in it, vanishes with the child, so no cleanup is needed either
// way. The result is cached (function-local static) since a container
// joining several networks in one run would otherwise probe once per join
// for no reason -- this is a fixed fact about the running kernel, not
// something that varies per network.
bool probe_veth_support();
// A network should use veth to join a container (rather than the
// network_tap_relay.h fallback) exactly when its own `veth` policy flag
// allows it *and* the kernel actually supports veth -- both must hold, the
// same "policy and capability are independent gates" pattern
// namespace_policy_enabled() (bwrap.cpp) already uses for --unshare-xxx.
bool should_use_veth(const NetworkEntry& network);
// Ensures `network`'s bridge (and, for `intern`, its dedicated persistent
// namespace -- persistent_netns.h) exists and is configured, creating
// whatever's missing: