Add global.with-veth/with-ipv6 config defaults for -n/--network creation

Replaces --no-ipv6/--no-veth (plain flags) with --with-ipv6/--with-veth,
each taking an explicit true/false value (e.g. --with-veth=false), parsed
via the same parse_bool_flag() the config file itself already uses (now
exported from config_file.h so cli_args.cpp can reuse it).

create_network_command() now resolves ipv6/veth as CLI flag -> config's own
global.with-ipv6/global.with-veth -> true, so a host that always wants the
tap+relay fallback (or no IPv6) can set it once in the config instead of
passing the flag on every network creation. -w/--write-config fills in both
new keys like the existing six unshare-* bools.
This commit is contained in:
2026-09-05 08:36:12 +00:00
parent 1f52bc5f6f
commit cdf9dcd210
10 changed files with 287 additions and 112 deletions
+60 -32
View File
@@ -75,20 +75,34 @@ Source layout (all under `src/`):
consumption needed, `'n'`'s own `case` just does
`network_specs.push_back(optarg)`. The same post-loop split as `-v` decides
`Mode::network` (standalone, exactly one occurrence) vs. join-with-`-r`
(repeatable, no limit). `--extern`/`--intern`/`--subnet <cidr>`/`--no-ipv6`/
`--subnet6 <cidr>`/`--no-veth` (`ParsedArgs::network_extern_flag`/`network_intern_flag`/
`network_subnet_flag`/`network_no_ipv6_flag`/`network_subnet6_flag`/
`network_no_veth_flag`) only
(repeatable, no limit). `--extern`/`--intern`/`--subnet <cidr>`/`--with-ipv6`/
`--subnet6 <cidr>`/`--with-veth` (`ParsedArgs::network_extern_flag`/`network_intern_flag`/
`network_subnet_flag`/`network_with_ipv6_flag`/`network_subnet6_flag`/
`network_with_veth_flag`) only
apply to the standalone (create) case and are rejected with a clear error if
given any other way (e.g. alongside `-r`) — `Mode::network` additionally
requires exactly one of `--extern`/`--intern`. `--no-veth` forces
`NetworkEntry::veth` (`config_file.h`) to `false` at creation time,
overriding the default `true` — see `network_bridge.h`'s
`probe_veth_support()`/`should_use_veth()` for what this controls: lets
the tap+relay fallback (the real target device's kernel lacks `CONFIG_VETH`
— see `docs/networking-design.md`'s addendum) be exercised on a
veth-capable machine like this dev box, without needing the actual
veth-less hardware. **`-n` used to belong to
requires exactly one of `--extern`/`--intern`. Unlike the plain boolean
flags elsewhere in this file, `--with-ipv6`/`--with-veth` are
`required_argument` (e.g. `--with-veth=false`), parsed via
`parse_bool_flag()` (`config_file.h` — exported specifically so this file
can reuse the exact same accepted forms, `"1"/"on"/"yes"/"true"` and
`"0"/"off"/"no"/"false"`, as the config file itself, rather than a second,
drifting copy) into `ParsedArgs::network_with_ipv6_flag`/`network_with_veth_flag`
(`std::optional<bool>``nullopt` means "not given on the CLI, use the
config file's own default", not "false"). `--with-veth=false` forces
`NetworkEntry::veth` (`config_file.h`) to `false` at creation time; when
neither is given, `create_network_command()` (`commands.cpp`) falls back to
`AppConfig::with_veth`/`with_ipv6` (`config_file.h`'s own two new
`global.with-veth`/`global.with-ipv6` keys, same "unset means enabled"
convention as the six `unshare-*` keys), only defaulting to `true` if that,
too, is unset — see `network_bridge.h`'s
`probe_veth_support()`/`should_use_veth()` for what a resolved `false`
controls: lets the tap+relay fallback (the real target device's kernel
lacks `CONFIG_VETH` — see `docs/networking-design.md`'s addendum) be
exercised on a veth-capable machine like this dev box, without needing the
actual veth-less hardware, or be made this dev box's own default via the
config file instead of passing `--with-veth=false` on every `-n --extern`/
`--intern` invocation. **`-n` used to belong to
`--no-nsenter`**: reassigned here since `--network` will be far more
heavily used; `--no-nsenter` moved to long-option-only (`options::no_nsenter`)
rather than hunting for a new letter, matching `--kill`'s own "rare/niche
@@ -200,7 +214,8 @@ Source layout (all under `src/`):
`create_volume_command()`/`delete_volume_command()`'s use of
`write_config_file()` (which only ever persists `AppConfig` fields that are
already set), this fills in *every* field before writing — the six
`unshare-*` bools via `.value_or(true)`, and `log_level` from the actually
`unshare-*` bools plus `with-veth`/`with-ipv6`, all via `.value_or(true)`,
and `log_level` from the actually
active `spdlog::get_level()` (not merely a default for when unset — this
also captures an explicit `--log-level` passed alongside `-w` on the same
command line, overriding whatever an existing config file's own
@@ -1666,7 +1681,7 @@ Source layout (all under `src/`):
`host.containers.internal` file (`dns-internal-hosts/<container>-<pid>`,
likewise just deleted) — called from `clean_processes_command()`
(`commands.cpp`) alongside the three existing sweeps. `--no-dns`
(`cli_args.{h,cpp}`, long-option only, same precedent as `--no-veth`) opts
(`cli_args.{h,cpp}`, long-option only, plain boolean flag, no value) opts
out even when `dnsmasq` is available. `build_bwrap_args()`/`run_bwrap()`
(`bwrap.{h,cpp}`) gained a `inject_dns_resolv_conf` bool, resolved once in
`run_container()` from `!network_specs.empty() && is_dnsmasq_available() &&
@@ -2054,20 +2069,32 @@ Source layout (all under `src/`):
the local YAML config file located by `config_file_path()`
(`$XDG_CONFIG_HOME/slocker-lite/config.yaml`,
falling back to `$HOME/.config/slocker-lite/config.yaml`). Supported `global` keys:
`log-level`, and six `unshare-<type>` keys (`unshare-user`/`unshare-ipc`/
`log-level`; six `unshare-<type>` keys (`unshare-user`/`unshare-ipc`/
`unshare-pid`/`unshare-net`/`unshare-uts`/`unshare-cgroup`, one per
`bwrap.cpp`'s own `namespace_probes` entry) controlling whether `-r/--run`
requests each of bwrap's `--unshare-xxx` flags — every other long option is a
one-shot flag, not a setting, so it doesn't belong in a persistent config file.
Each `unshare-*` key accepts `"1"`/`"on"`/`"yes"`/`"true"` (enabled) or
`"0"`/`"off"`/`"no"`/`"false"` (disabled), case-insensitively (`.cpp`-local
`parse_bool_flag()`); an unset key defaults to enabled, and an unrecognized
value logs a `spdlog::warn` and is treated as unset (default enabled) rather
than failing the whole config load — consistent with this file's existing
forward-compatible/ignore-malformed-entries policy (only malformed *YAML
syntax* is a hard error). A missing file returns a default-constructed
(empty) `AppConfig`, not an error; unknown sections/keys (and malformed
individual volume entries) are likewise ignored for forward-compatibility.
requests each of bwrap's `--unshare-xxx` flags; and two `with-<feature>` keys
(`with-veth`/`with-ipv6`, `AppConfig::with_veth`/`with_ipv6`) giving
`-n/--network`'s own creation-time `veth`/`ipv6` policy (`NetworkEntry`,
above) a persistent default, used whenever the corresponding
`--with-veth`/`--with-ipv6` CLI flag (`cli_args.{h,cpp}`, see above) isn't
given — every other long option is a one-shot flag, not a setting, so it
doesn't belong in a persistent config file. All eight of these boolean keys
share one small `.cpp`-local `BoolGlobalKey {key, field}` pairing table (two
arrays, `unshare_keys` and `network_default_keys`, both consumed by shared
`load_bool_keys()`/`write_bool_keys()` helpers rather than repeating the same
find-parse-or-warn / emit-if-set loop body per group) and accept
`"1"`/`"on"`/`"yes"`/`"true"` (enabled) or `"0"`/`"off"`/`"no"`/`"false"`
(disabled), case-insensitively — `parse_bool_flag()`, exported (not just this
file's own internal helper) specifically so `cli_args.cpp`'s own
`--with-ipv6`/`--with-veth` value parsing accepts exactly the same forms as
the config file itself, rather than a second, drifting copy. An unset key
defaults to enabled, and an unrecognized value logs a `spdlog::warn` and is
treated as unset (default enabled) rather than failing the whole config load
— consistent with this file's existing forward-compatible/ignore-malformed-
entries policy (only malformed *YAML syntax* is a hard error). A missing file
returns a default-constructed (empty) `AppConfig`, not an error; unknown
sections/keys (and malformed individual volume entries) are likewise ignored
for forward-compatibility.
`main()` applies `config->log_level` (via the existing `apply_log_level()`)
right after `spdlog::cfg::load_env_levels()` and before parsing CLI options,
so an explicit `--log-level` on the command line always overwrites it
@@ -2076,8 +2103,8 @@ Source layout (all under `src/`):
document-building/emitter API, symmetric to the read side) — used by
`-v/--volume` (`create_volume_command()`, `commands.cpp`) to persist a new
`VolumeEntry {name, directory}` into the `volumes` section, preserving
`global` (including any set `unshare-*` keys, re-serialized as canonical
`"true"`/`"false"`) untouched. **`VolumeEntry`/the `volumes` section is a
`global` (including any set `unshare-*`/`with-veth`/`with-ipv6` keys,
re-serialized as canonical `"true"`/`"false"`) untouched. **`VolumeEntry`/the `volumes` section is a
distinct concept from `OciImageConfig::volumes`**: this is a user-defined
`name -> host directory` mapping created via `-v/--volume`, not an image's
own declared mount points (still unconsumed, see `oci_image.{h,cpp}` above).
@@ -2106,8 +2133,9 @@ Source layout (all under `src/`):
`ipv6` (`parse_bool_flag()`, written as canonical `"true"`/`"false"`,
always written regardless of value — unlike `subnet6`, there's no
companion field whose presence depends on it) — see `network_bridge.h`'s
`probe_veth_support()`/`should_use_veth()` above and `--no-veth`
(`cli_args.{h,cpp}`) below for what it controls.
`probe_veth_support()`/`should_use_veth()` above and `--with-veth`/
`global.with-veth` (`cli_args.{h,cpp}`/`config_file.{h,cpp}`) below for
what it controls.
- `network_subnet.{h,cpp}` — pure CIDR arithmetic backing `-n/--network`'s
subnet allocation and `network_bridge.{h,cpp}`'s (see below) gateway-address
computation; no kernel/`ip`/`iptables` calls of its own. `is_valid_network_name()`
@@ -2253,8 +2281,8 @@ Build directory is `buildDir/` (already configured).
full flag list: `-m/--mount`, `-r/--run`, `-u/--umount`, `-c/--cleanup`,
`-l/--list-images`, `-i/--inspect`, `-x/--exec`, `--kill`, `--no-nsenter`, `-D/--daemonize`,
`--user`, `--group`, `--hostname`, `--env`, `--env-file`, `-v/--volume`, `--list-volumes`, `--delete-volume`,
`--delete-volume-full`, `-n/--network`, `--extern`, `--intern`, `--subnet`, `--no-ipv6`, `--subnet6`,
`--no-veth`, `--list-networks`, `--delete-network`, `-p/--port-forward`, `--no-dns`, `--list-processes`, `--clean-processes`,
`--delete-volume-full`, `-n/--network`, `--extern`, `--intern`, `--subnet`, `--with-ipv6`, `--subnet6`,
`--with-veth`, `--list-networks`, `--delete-network`, `-p/--port-forward`, `--no-dns`, `--list-processes`, `--clean-processes`,
`-w/--write-config`, `-t/--test [-- <catch-command-line-options>]`, `--log-level`,
`-h/--help`, `-V/--version`)
- Run tests: `meson test -C buildDir` (the `[unit]` + safe `[integration]` categories