From a464de911c468287e7f014388a9137a6d37c8674 Mon Sep 17 00:00:00 2001 From: Kei Kamikawa Date: Thu, 15 Oct 2026 01:21:46 +0900 Subject: [PATCH] feat(network): enable IPv6 in guest images --- README.md | 6 +++++- busybox_config/config | 4 ++-- kconfig/aarch64.config | 2 +- kconfig/x86_64.config | 2 +- tests/README.md | 10 +++++++++- tests/smoke.py | 28 +++++++++++++++++++++++++--- 6 files changed, 43 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 57efb44..bcce038 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,9 @@ minimal configs in `kconfig/`; new kernel options take their upstream defaults. What's supported: -- Internet +- IPv4 and IPv6 networking + - IPv4 DHCP + - IPv6 stateless address autoconfiguration (SLAAC) - VIRTIO (Virt device I/O) - virtio blk device - virtio console (via hvc drivers. e.g. `console=hvc0`) @@ -26,6 +28,8 @@ What's supported: ## Initramfs +BusyBox includes IPv6 support and `ping6` for checking guest connectivity. + Our initramfs has a very simple init system controlled by `fs/init` script on this repository root. this script will do: - Mount any file systems (proc, sysfs and devfs). diff --git a/busybox_config/config b/busybox_config/config index 7e57f03..72b1d2c 100644 --- a/busybox_config/config +++ b/busybox_config/config @@ -856,7 +856,7 @@ CONFIG_TREE=y # # Networking Utilities # -# CONFIG_FEATURE_IPV6 is not set +CONFIG_FEATURE_IPV6=y # CONFIG_FEATURE_UNIX_LOCAL is not set # CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set @@ -958,7 +958,7 @@ CONFIG_FEATURE_NSLOOKUP_LONG_OPTIONS=y # CONFIG_FEATURE_NTPD_CONF is not set # CONFIG_FEATURE_NTP_AUTH is not set CONFIG_PING=y -# CONFIG_PING6 is not set +CONFIG_PING6=y # CONFIG_FEATURE_FANCY_PING is not set # CONFIG_PSCAN is not set CONFIG_ROUTE=y diff --git a/kconfig/aarch64.config b/kconfig/aarch64.config index 4a711da..062f5e8 100644 --- a/kconfig/aarch64.config +++ b/kconfig/aarch64.config @@ -69,7 +69,7 @@ CONFIG_PACKET=y CONFIG_UNIX=y CONFIG_INET=y # CONFIG_INET_DIAG is not set -# CONFIG_IPV6 is not set +# CONFIG_IPV6_SIT is not set CONFIG_NETFILTER=y CONFIG_NETFILTER_NETLINK_QUEUE=y CONFIG_VSOCKETS=y diff --git a/kconfig/x86_64.config b/kconfig/x86_64.config index 9226c25..07c865b 100644 --- a/kconfig/x86_64.config +++ b/kconfig/x86_64.config @@ -47,7 +47,7 @@ CONFIG_IP_PNP=y CONFIG_IP_PNP_DHCP=y CONFIG_IP_PNP_BOOTP=y CONFIG_IP_PNP_RARP=y -# CONFIG_IPV6 is not set +# CONFIG_IPV6_SIT is not set CONFIG_NETFILTER=y CONFIG_NETFILTER_NETLINK_QUEUE=y CONFIG_VSOCKETS=y diff --git a/tests/README.md b/tests/README.md index 0f9d863..519db40 100644 --- a/tests/README.md +++ b/tests/README.md @@ -35,11 +35,19 @@ Userspace and release archives are rebuilt on every run. The suite checks config stability and config-update failure handling, and boots the resulting release archives with 256 MiB of RAM. VM checks cover -the kernel version, DHCP, password SSH login, virtiofs read/write and read-only +the kernel version, IPv4 DHCP, IPv6 SLAAC, an IPv6 default route, password SSH +login, virtiofs read/write and read-only exports, console and RNG devices, the SSH banner over default/custom vsock ports, and guest poweroff. +QEMU user networking advertises `fd00:2::/64` and uses `fd00:2::2` as its IPv6 +router. The guest has up to 10 seconds to acquire a non-tentative global address +and the default route, then runs `ping6 fd00:2::2`. BusyBox's minimal `ping6` +exits after the first reply or its five-second timeout. These checks use only +the local QEMU network, so they require no Internet access or upstream IPv6. + Logs are written to `build-tests/`, including QEMU and guest console output. +The IPv6 address, route and ping output are saved in `ipv6.log`. Actions uploads these logs even on failure and uploads the archives only after all tests pass. Source downloads and build directories remain available for local reruns. For an existing build, rerun one VM test with: diff --git a/tests/smoke.py b/tests/smoke.py index 03fecf1..c127dc0 100644 --- a/tests/smoke.py +++ b/tests/smoke.py @@ -42,7 +42,7 @@ def ssh(command): ["sshpass", "-p", "passwd", "ssh", "-o", "StrictHostKeyChecking=no", "-o", f"UserKnownHostsFile={work}/known_hosts", "-o", "ConnectTimeout=3", "-p", str(port), "root@127.0.0.1", command], - capture_output=True, text=True, timeout=15, + capture_output=True, text=True, timeout=30, ) try: @@ -82,7 +82,8 @@ def ssh(command): "-numa", "node,memdev=mem", "-device", "virtio-serial-pci", "-chardev", f"file,id=console,path={logs_dir}/console.log", "-device", "virtconsole,chardev=console", "-netdev", - f"user,id=net,hostfwd=tcp:127.0.0.1:{port}-:22", + f"user,id=net,hostfwd=tcp:127.0.0.1:{port}-:22," + "ipv4=on,ipv6=on,ipv6-net=fd00:2::/64,ipv6-host=fd00:2::2", "-device", "virtio-net-pci,netdev=net,romfile=", "-device", "virtio-rng-pci", "-device", "virtio-balloon-pci", "-device", f"vhost-vsock-pci,guest-cid={cid}"] @@ -107,6 +108,26 @@ def ssh(command): else: raise RuntimeError(f"Guest SSH did not become ready; see {logs_dir}") result = ssh('''set -eu +attempt=0 +while [ "$attempt" -lt 10 ]; do + addresses=$(ip -6 addr show dev eth0) + routes=$(ip -6 route show) + if echo "$addresses" | grep 'inet6 fd00:2:' | grep 'scope global' | grep -v tentative >/dev/null && + echo "$routes" | grep -q '^default via fe80:.* dev eth0'; then + break + fi + attempt=$((attempt + 1)) + sleep 1 +done +echo "$addresses" +echo "$routes" +test "$attempt" -lt 10 +ping6 fd00:2::2 +''') + (logs_dir / "ipv6.log").write_text(result.stdout + result.stderr) + if result.returncode: + raise RuntimeError(f"Guest IPv6 checks failed; see {logs_dir}") + result = ssh('''set -eu ip addr show eth0 mkdir -p /mnt/rw /mnt/ro mount -t virtiofs rw /mnt/rw @@ -138,7 +159,8 @@ def ssh(command): ssh("poweroff") if vm.wait(timeout=30) != 0: raise RuntimeError("Guest shutdown failed") - print(f"{args.arch}: {args.kernel_version}, DHCP, SSH, virtiofs RW/RO, " + print(f"{args.arch}: {args.kernel_version}, IPv4 DHCP, IPv6 SLAAC/route/ping, " + "SSH, virtiofs RW/RO, " f"vsock {vsock_port}, poweroff: PASS", flush=True) finally: for process in reversed(processes):