diff --git a/CLAUDE.md b/CLAUDE.md index d6725fd..008de2a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -177,8 +177,11 @@ Source layout (all under `src/`): the entry appended to `config.networks` and persisted; a network that fails to provision isn't saved. `list_networks_command()` reuses the same independently-per-column tab-alignment scheme as `list_processes_command()` - (name/kind/subnet each aligned, then the IPv6 subnet — or `"(no ipv6)"` — - appended unaligned as the trailing column, nothing follows it). + (name/kind/subnet/bridge each aligned — the bridge name recomputed via + `bridge_name(network.name)` — `network_bridge.h` — rather than stored, since + 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 diff --git a/README.md b/README.md index fc9a12d..e2f7545 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ slocker-lite -V|--version | `--delete-volume ` | Remove a named volume from the config. The host directory is left untouched. | | `--delete-volume-full ` | Like `--delete-volume`, but also recursively deletes the volume's host directory. | | `-n, --network ` | Create/manage a persistent named network: requires exactly one of `--extern` (a real Linux bridge in the host's own namespace, with NAT/forwarding set up so containers on it reach the host's real network) or `--intern` (a bridge inside its own dedicated, routeless namespace, only reachable by other containers on the same network). `--subnet ` overrides the auto-allocated IPv4 range (`10.168.0.0/24`, incrementing per network); `--no-ipv6` disables (and `--subnet6 ` overrides) the auto-allocated IPv6 range, on by default. `--no-veth` forces the tap+relay join fallback even on a kernel that supports veth (useful for testing that path; it's otherwise chosen automatically whenever the running kernel lacks veth support). With `--run`, instead joins `` to the container as its own `eth` interface with an address from the network's subnet; repeatable, no membership limit. Root-only for now. **Known gap**: on a network joined via the tap+relay fallback, peer-to-peer connectivity works, but reaching the network's own gateway (and thus the outside, for `extern`) does not yet — root cause unconfirmed, see [`docs/networking-design.md`](docs/networking-design.md). | -| `--list-networks` | List all named networks (see `-n/--network`) with their kind, IPv4 subnet, and IPv6 subnet (or `(no ipv6)`). | +| `--list-networks` | List all named networks (see `-n/--network`) with their kind, IPv4 subnet, bridge name, and IPv6 subnet (or `(no ipv6)`). | | `--delete-network ` | Remove a named network from the config. | | `-p, --port-forward [:]:` | With `--run`, forward a TCP port from the host into the container. `` is optional, defaulting to the container's sole `--extern` network (an error if it joined more than one without specifying). Repeatable. Reachable via the host's real, externally-facing IP; `localhost`/loopback access has a known NAT-hairpinning limitation (see [`docs/networking-design.md`](docs/networking-design.md)). | | `--list-processes` | List running `--run` sessions found by their pid files under `$XDG_STATE_HOME/slocker-lite/run/`, with their pid, container name, and status (`running` or `exited`). | diff --git a/src/cli_args.cpp b/src/cli_args.cpp index 5064fd8..8683667 100644 --- a/src/cli_args.cpp +++ b/src/cli_args.cpp @@ -233,8 +233,8 @@ void print_usage(const char* prog) { " --run, instead join to the container;\n" " may be repeated, no membership limit\n" " --list-networks list all named networks (see -n/--network)\n" - " with their kind, IPv4 subnet, and IPv6 subnet\n" - " (or \"(no ipv6)\")\n" + " with their kind, IPv4 subnet, bridge name,\n" + " and IPv6 subnet (or \"(no ipv6)\")\n" " --delete-network \n" " remove a named network from the config\n" " -p, --port-forward [:]:\n" diff --git a/src/commands.cpp b/src/commands.cpp index 4549442..fba13b7 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -467,25 +467,31 @@ int list_networks_command(const AppConfig& config) { std::vector names; std::vector kinds; std::vector subnets; + std::vector bridges; names.reserve(config.networks.size()); kinds.reserve(config.networks.size()); subnets.reserve(config.networks.size()); + bridges.reserve(config.networks.size()); size_t max_name_len = 0; size_t max_kind_len = 0; size_t max_subnet_len = 0; + size_t max_bridge_len = 0; for (const auto& network : config.networks) { names.push_back(network.name); kinds.push_back(network.kind == NetworkKind::extern_ ? "extern" : "intern"); subnets.push_back(network.subnet); + bridges.push_back(bridge_name(network.name)); max_name_len = std::max(max_name_len, names.back().size()); max_kind_len = std::max(max_kind_len, kinds.back().size()); max_subnet_len = std::max(max_subnet_len, subnets.back().size()); + max_bridge_len = std::max(max_bridge_len, bridges.back().size()); } // Independently tracked per column, same scheme as list_processes_command(). size_t name_target_tabs = max_name_len / tab_width + 1; size_t kind_target_tabs = max_kind_len / tab_width + 1; size_t subnet_target_tabs = max_subnet_len / tab_width + 1; + size_t bridge_target_tabs = max_bridge_len / tab_width + 1; for (size_t i = 0; i < config.networks.size(); ++i) { size_t name_tabs_used = names[i].size() / tab_width; @@ -494,8 +500,11 @@ int list_networks_command(const AppConfig& config) { size_t kind_tabs_needed = kind_target_tabs > kind_tabs_used ? kind_target_tabs - kind_tabs_used : 1; size_t subnet_tabs_used = subnets[i].size() / tab_width; size_t subnet_tabs_needed = subnet_target_tabs > subnet_tabs_used ? subnet_target_tabs - subnet_tabs_used : 1; - fmt::print("{}{}{}{}{}{}{}\n", names[i], std::string(name_tabs_needed, '\t'), kinds[i], + size_t bridge_tabs_used = bridges[i].size() / tab_width; + size_t bridge_tabs_needed = bridge_target_tabs > bridge_tabs_used ? bridge_target_tabs - bridge_tabs_used : 1; + fmt::print("{}{}{}{}{}{}{}{}{}\n", names[i], std::string(name_tabs_needed, '\t'), kinds[i], std::string(kind_tabs_needed, '\t'), subnets[i], std::string(subnet_tabs_needed, '\t'), + bridges[i], std::string(bridge_tabs_needed, '\t'), config.networks[i].ipv6 ? config.networks[i].subnet6 : std::string("(no ipv6)")); } return 0;