libkrunfw: enable netfilter + bridge for docker --network=bridge

dockerd / podman default to bridge networking, which needs the
in-kernel bridge driver to set up `docker0` plus iptables (the
legacy backend here; the nf_tables backend is a follow-up hunk)
for SNAT/MASQUERADE between containers and the outside. Without
these the daemon starts but every container start fails on
`Table does not exist (do you need to insmod?)`.

Verified in a nested guest on the rebuilt libkrunfw:
- `ip link add docker0 type bridge` (was ENODEV)
- `iptables-legacy -t nat -A POSTROUTING -j MASQUERADE`
- `iptables -t nat -L` (no longer "Failed to initialize nft")

Cost on a clean v5.2.1 rebuild: +348 KB bzImage, +0 on the shipped
`.so` (the new code fits within the 64K page padding `bin2cbundle`
already adds). 15-run nested-boot mean +5 ms vs the mqueue commit
— within 1σ noise.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
agent-vm 2026-05-26 20:49:28 +00:00
parent 75297b6db9
commit eadd712f06
2 changed files with 51 additions and 13 deletions

View file

@ -19,24 +19,31 @@ Stock libkrunfw ships only the paravirt-guest helpers
can't host its own. `agent-vm` needs the in-kernel KVM hypervisor for
Docker-in-Docker / nested KVM use cases, so we flip:
| symbol | reason |
| symbol(s) | reason |
|---|---|
| `CONFIG_KVM=y` | enable the in-kernel KVM hypervisor |
| `CONFIG_KVM_INTEL=y` | Intel VMX backend |
| `CONFIG_KVM_AMD=y` | AMD SVM backend |
| `CONFIG_POSIX_MQUEUE=y` | runc/crun default OCI spec always mounts `/dev/mqueue`; without this every container start fails with `mount mqueue: no such device` |
| `CONFIG_KVM=y` + `CONFIG_KVM_INTEL=y` + `CONFIG_KVM_AMD=y` | in-kernel KVM hypervisor for nested KVM use cases |
| `CONFIG_POSIX_MQUEUE=y` | runc/crun default OCI spec always mounts `/dev/mqueue`; without it every container start fails with `mount mqueue: no such device` |
| netfilter + bridge family (`NETFILTER`, `NF_CONNTRACK`, `NF_NAT`, `IP_NF_*`, `IP6_NF_*`, `BRIDGE`, `BRIDGE_NETFILTER`, `VLAN_8021Q`, …) | docker/podman `--network=bridge` (the default) needs iptables and the bridge driver to set up `docker0` + SNAT. Without these the daemon starts but every container start fails on `Table does not exist` |
`olddefconfig` pulls in the rest (`KVM_X86`, `KVM_SMM`, `KVM_HYPERV`,
`KVM_VFIO`, `POSIX_MQUEUE_SYSCTL`, …) automatically.
`olddefconfig` pulls in the rest of each cascade (`KVM_X86`, `KVM_SMM`,
`KVM_HYPERV`, `KVM_VFIO`, `POSIX_MQUEUE_SYSCTL`, `NETFILTER_NETLINK`,
`NF_DEFRAG_IPV4`/`IPV6`, `BRIDGE_IGMP_SNOOPING`, …) automatically.
`devtmpfs` and `DEVTMPFS_MOUNT` are already enabled upstream so
`/dev/kvm` materializes at boot without further config changes.
## Cost of each override
Measured by rebuilding the `.so` from a pristine v5.2.1 checkout with and
without each hunk (Debian 13 toolchain, gcc 14.2.0):
Measured by rebuilding the `.so` from a pristine v5.2.1 checkout with each
hunk added incrementally (Debian 13 toolchain, gcc 14.2.0). Boot time is
15-run mean of a nested `msb run alpine -- /bin/true`, sd ≈ 1720 ms:
| change | `bzImage` Δ | stripped `vmlinux` Δ | `libkrunfw.so` Δ | boot Δ |
|---|---|---|---|---|
| `+POSIX_MQUEUE` | +12 KB (+0.17%) | +4 KB (+0.02%) | +64 KB (+0.31%, page padding) | none (15-run mean within noise on nested boot) |
| cumulative change | `bzImage` | `libkrunfw.so` (shipped) | nested-boot mean |
|---|---|---|---|
| baseline (just `+KVM`) | 7,431 KB | 21,300 KB | 1.015 s |
| `+POSIX_MQUEUE` | +12 KB | +64 KB (page padding) | +2 ms |
| `+netfilter + bridge` | +348 KB | +0 (still fits in mqueue's page) | +5 ms |
The shipped `.so` is what affects npm-package size and `agent-vm setup` cold-pull
time; the `bzImage` figure is informational. Boot-time deltas were all within
1σ of baseline noise.

View file

@ -18,9 +18,40 @@
CONFIG_AS_AVX512=y
CONFIG_AS_SHA1_NI=y
CONFIG_AS_SHA256_NI=y
@@ -2389,3 +2389,5 @@
@@ -2389,3 +2389,36 @@
#
# end of Rust hacking
# end of Kernel hacking
+CONFIG_KVM_INTEL=y
+CONFIG_KVM_AMD=y
+
+# Netfilter + bridge for docker/podman `--network=bridge` (the
+# default). Covers the iptables-legacy backend; the nf_tables
+# backend's `NF_TABLES` / `NFT_*` are in a follow-up patch hunk.
+# `olddefconfig` resolves the rest of each dependency cascade
+# (`NETFILTER_NETLINK`, `NF_DEFRAG_IPV4`/`IPV6`, `BRIDGE_IGMP_SNOOPING`, …).
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_ADVANCED=y
+CONFIG_NETFILTER_XTABLES=y
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_NAT=y
+CONFIG_NETFILTER_XT_TARGET_MASQUERADE=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_NAT=y
+CONFIG_NF_NAT_MASQUERADE=y
+CONFIG_NF_NAT_REDIRECT=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_NAT=y
+CONFIG_IP6_NF_TARGET_MASQUERADE=y
+CONFIG_BRIDGE=y
+CONFIG_BRIDGE_NETFILTER=y
+CONFIG_LLC=y
+CONFIG_STP=y
+CONFIG_VLAN_8021Q=y