Just a few tiny things I forgot in the last series. Changes since v3: - Remove leftover c-lib target from start-vmm meson.build - Update Rust dependencies including tokio-vhost & vhost-device-net Yureka Lilian (10): release/checks/integration: temporarily disable networking tests vm/sys/net: remove connman pkgs: temporarily disable mailutils tests vm/sys/net: integrate xdp-forwarder vm/sys/net: add iwd tools: add spectrum-router host: integrate router img/app: change to ipv6 nameserver release/checks/integration: Adapt networking test for ipv6 release/checks/integration: reenable networking tests host/rootfs/default.nix | 4 +- host/rootfs/file-list.mk | 2 + .../data/service/spectrum-router/down | 0 .../template/data/service/spectrum-router/run | 9 + host/rootfs/image/usr/bin/run-vmm | 21 +- host/rootfs/image/usr/bin/vm-import | 13 - img/app/image/etc/mdev/iface | 19 +- img/app/image/etc/resolv.conf | 2 +- .../etc/s6-rc/mdevd/dependencies.d/nftables | 0 pkgs/default.nix | 2 + pkgs/overlay.nix | 4 + release/checks/integration/default.nix | 2 +- release/checks/integration/networking.c | 33 +- tools/router/Cargo.lock | 807 ++++++++++++++++++ tools/router/Cargo.lock.license | 2 + tools/router/Cargo.toml | 21 + tools/router/default.nix | 18 + tools/router/src/main.rs | 73 ++ tools/router/src/packet.rs | 187 ++++ tools/router/src/protocol.rs | 65 ++ tools/router/src/router.rs | 147 ++++ tools/router/src/upstream.rs | 175 ++++ tools/start-vmm/ch.rs | 66 +- tools/start-vmm/lib.rs | 73 +- tools/start-vmm/meson.build | 9 +- tools/start-vmm/net-util.c | 39 - tools/start-vmm/net-util.h | 6 - tools/start-vmm/net.c | 55 -- tools/start-vmm/net.rs | 11 - tools/start-vmm/tests/meson.build | 5 - .../start-vmm/tests/tap_open-name-too-long.c | 20 - tools/start-vmm/tests/tap_open.c | 28 - vm/sys/net/Makefile | 2 +- vm/sys/net/default.nix | 15 +- vm/sys/net/file-list.mk | 3 - vm/sys/net/image/etc/fstab | 2 + vm/sys/net/image/etc/mdev/iface | 28 +- vm/sys/net/image/etc/nftables.conf | 16 +- vm/sys/net/image/etc/s6-rc/connman/run | 19 - vm/sys/net/image/etc/s6-rc/connman/type | 1 - .../net/image/etc/s6-rc/connman/type.license | 2 - 41 files changed, 1640 insertions(+), 366 deletions(-) rename vm/sys/net/image/etc/s6-rc/connman/dependencies.d/dbus => host/rootfs/image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/spectrum-router/down (100%) create mode 100755 host/rootfs/image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/spectrum-router/run create mode 100644 img/app/image/etc/s6-rc/mdevd/dependencies.d/nftables create mode 100644 tools/router/Cargo.lock create mode 100644 tools/router/Cargo.lock.license create mode 100644 tools/router/Cargo.toml create mode 100644 tools/router/default.nix create mode 100644 tools/router/src/main.rs create mode 100644 tools/router/src/packet.rs create mode 100644 tools/router/src/protocol.rs create mode 100644 tools/router/src/router.rs create mode 100644 tools/router/src/upstream.rs delete mode 100644 tools/start-vmm/net-util.c delete mode 100644 tools/start-vmm/net-util.h delete mode 100644 tools/start-vmm/net.c delete mode 100644 tools/start-vmm/tests/tap_open-name-too-long.c delete mode 100644 tools/start-vmm/tests/tap_open.c delete mode 100644 vm/sys/net/image/etc/s6-rc/connman/run delete mode 100644 vm/sys/net/image/etc/s6-rc/connman/type delete mode 100644 vm/sys/net/image/etc/s6-rc/connman/type.license -- 2.51.2
The following commits only work as a series. Signed-off-by: Yureka Lilian <yureka@cyberchaos.dev> --- release/checks/integration/meson.build | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/release/checks/integration/meson.build b/release/checks/integration/meson.build index 7bf8f51..8938728 100644 --- a/release/checks/integration/meson.build +++ b/release/checks/integration/meson.build @@ -11,7 +11,12 @@ run_qemu = find_program('../../../scripts/run-qemu.sh') lib = static_library('spectrum-integration-test', 'lib.c') -foreach test : ['appimage', 'late-serial', 'networking', 'portal'] +foreach test : [ + 'appimage', + 'late-serial', + # 'networking', + 'portal', +] test(test, executable(test, test + '.c', link_with : lib), timeout : 400, args : [run_qemu]) -- 2.51.2
This patch has been committed as a8794ccbfb1ec8c5e2c73def590bd70ab288d790, which can be viewed online at https://spectrum-os.org/git/spectrum/commit/?id=a8794ccbfb1ec8c5e2c73def590b.... This is an automated message. Send comments/questions/requests to: Alyssa Ross <hi@alyssa.is>
In preparation to integrating xdp-forwarder, making the net-vm a net-driver VM. dbus is left, as it will likely be needed in the future for iwd. Signed-off-by: Yureka Lilian <yureka@cyberchaos.dev> Reviewed-by: Alyssa Ross <hi@alyssa.is> --- vm/sys/net/Makefile | 2 +- vm/sys/net/default.nix | 8 +++----- vm/sys/net/file-list.mk | 3 --- .../etc/s6-rc/connman/dependencies.d/dbus | 0 vm/sys/net/image/etc/s6-rc/connman/run | 19 ------------------- vm/sys/net/image/etc/s6-rc/connman/type | 1 - .../net/image/etc/s6-rc/connman/type.license | 2 -- 7 files changed, 4 insertions(+), 31 deletions(-) delete mode 100644 vm/sys/net/image/etc/s6-rc/connman/dependencies.d/dbus delete mode 100644 vm/sys/net/image/etc/s6-rc/connman/run delete mode 100644 vm/sys/net/image/etc/s6-rc/connman/type delete mode 100644 vm/sys/net/image/etc/s6-rc/connman/type.license diff --git a/vm/sys/net/Makefile b/vm/sys/net/Makefile index d71c232..7ad5e5c 100644 --- a/vm/sys/net/Makefile +++ b/vm/sys/net/Makefile @@ -29,7 +29,7 @@ $(vmdir)/netvm/blk/root.img: ../../../scripts/make-gpt.sh ../../../scripts/sfdis build/rootfs.erofs:root:ea21da27-0391-48da-9235-9d2ab2ca7844:root mv $@.tmp $@ -DIRS = dev etc/s6-linux-init/env proc run sys var/lib/connman +DIRS = dev etc/s6-linux-init/env proc run sys BUILD_FILES = build/etc/s6-rc diff --git a/vm/sys/net/default.nix b/vm/sys/net/default.nix index de273e5..ae7fdc5 100644 --- a/vm/sys/net/default.nix +++ b/vm/sys/net/default.nix @@ -7,7 +7,7 @@ pkgsMusl.callPackage ( { lib, stdenvNoCC, nixos, runCommand, writeClosure , erofs-utils, jq, s6-rc, util-linux, xorg -, busybox, connmanMinimal, dbus, execline, kmod, linux_latest, mdevd, nftables +, busybox, dbus, execline, kmod, linux_latest, mdevd, nftables , s6, s6-linux-init }: @@ -51,10 +51,8 @@ let ]; }); - connman = connmanMinimal; - packages = [ - connman dbus execline kmod mdevd s6 s6-linux-init s6-rc + dbus execline kmod mdevd s6 s6-linux-init s6-rc (busybox.override { extraConfig = '' @@ -73,7 +71,7 @@ let # Packages that should be fully linked into /usr, # (not just their bin/* files). - usrPackages = [ connman dbus firmware kernel.modules terminfo ]; + usrPackages = [ dbus firmware kernel.modules terminfo ]; packagesSysroot = runCommand "packages-sysroot" { inherit packages; diff --git a/vm/sys/net/file-list.mk b/vm/sys/net/file-list.mk index 15ccd24..7cc3520 100644 --- a/vm/sys/net/file-list.mk +++ b/vm/sys/net/file-list.mk @@ -20,9 +20,6 @@ LINKS = \ image/var/run S6_RC_FILES = \ - image/etc/s6-rc/connman/dependencies.d/dbus \ - image/etc/s6-rc/connman/run \ - image/etc/s6-rc/connman/type \ image/etc/s6-rc/dbus/notification-fd \ image/etc/s6-rc/dbus/run \ image/etc/s6-rc/dbus/type \ diff --git a/vm/sys/net/image/etc/s6-rc/connman/dependencies.d/dbus b/vm/sys/net/image/etc/s6-rc/connman/dependencies.d/dbus deleted file mode 100644 index e69de29..0000000 diff --git a/vm/sys/net/image/etc/s6-rc/connman/run b/vm/sys/net/image/etc/s6-rc/connman/run deleted file mode 100644 index 058fc17..0000000 --- a/vm/sys/net/image/etc/s6-rc/connman/run +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/execlineb -P -# SPDX-License-Identifier: EUPL-1.2+ -# SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is> - -if { modprobe af_packet } - -backtick -E HARDWARE_INTERFACES { - pipeline { - find -L /sys/class/net -mindepth 2 -maxdepth 2 -name address -print0 - } - - # Filter out other VMs and the loopback device. - pipeline { xargs -0 grep -iL ^\\(02:01:\\|00:00:00:00:00:00$\\) } - - # Extract the interface names from the address file paths. - awk -F/ "{if (NR > 1) printf \",\"; printf \"%s\", $5}" -} - -connmand -ni $HARDWARE_INTERFACES diff --git a/vm/sys/net/image/etc/s6-rc/connman/type b/vm/sys/net/image/etc/s6-rc/connman/type deleted file mode 100644 index 5883cff..0000000 --- a/vm/sys/net/image/etc/s6-rc/connman/type +++ /dev/null @@ -1 +0,0 @@ -longrun diff --git a/vm/sys/net/image/etc/s6-rc/connman/type.license b/vm/sys/net/image/etc/s6-rc/connman/type.license deleted file mode 100644 index 2b3b032..0000000 --- a/vm/sys/net/image/etc/s6-rc/connman/type.license +++ /dev/null @@ -1,2 +0,0 @@ -SPDX-License-Identifier: CC0-1.0 -SPDX-FileCopyrightText: 2020 Alyssa Ross <hi@alyssa.is> -- 2.51.2
This patch has been committed as d0721e528aedc41ad3be928ababf4024c00e4c2a, which can be viewed online at https://spectrum-os.org/git/spectrum/commit/?id=d0721e528aedc41ad3be928ababf.... This is an automated message. Send comments/questions/requests to: Alyssa Ross <hi@alyssa.is>
pkgsMusl.mailutils fails to build, which has been tracked down to some impurity (fails to build on some systems consistently) Signed-off-by: Yureka Lilian <yureka@cyberchaos.dev> --- pkgs/overlay.nix | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/overlay.nix b/pkgs/overlay.nix index fdddae0..19a542c 100644 --- a/pkgs/overlay.nix +++ b/pkgs/overlay.nix @@ -11,4 +11,8 @@ ); skawarePackages = import ./skaware-packages { inherit final super; }; + + mailutils = super.mailutils.overrideAttrs (_: ( + final.lib.optionalAttrs final.stdenv.hostPlatform.isMusl { doCheck = false; } + )); }) -- 2.51.2
This patch has been committed as 6849d6e37da686fdb174001c637fe26fe7728843, which can be viewed online at https://spectrum-os.org/git/spectrum/commit/?id=6849d6e37da686fdb174001c637f.... This is an automated message. Send comments/questions/requests to: Alyssa Ross <hi@alyssa.is>
Signed-off-by: Yureka Lilian <yureka@cyberchaos.dev> Reviewed-by: Alyssa Ross <hi@alyssa.is> --- .../etc/s6-rc/mdevd/dependencies.d/nftables | 0 vm/sys/net/default.nix | 11 ++++++-- vm/sys/net/image/etc/fstab | 2 ++ vm/sys/net/image/etc/mdev/iface | 28 ++++++------------- vm/sys/net/image/etc/nftables.conf | 16 ++++++++--- 5 files changed, 31 insertions(+), 26 deletions(-) create mode 100644 img/app/image/etc/s6-rc/mdevd/dependencies.d/nftables diff --git a/img/app/image/etc/s6-rc/mdevd/dependencies.d/nftables b/img/app/image/etc/s6-rc/mdevd/dependencies.d/nftables new file mode 100644 index 0000000..e69de29 diff --git a/vm/sys/net/default.nix b/vm/sys/net/default.nix index ae7fdc5..f4e51dd 100644 --- a/vm/sys/net/default.nix +++ b/vm/sys/net/default.nix @@ -8,7 +8,7 @@ pkgsMusl.callPackage ( { lib, stdenvNoCC, nixos, runCommand, writeClosure , erofs-utils, jq, s6-rc, util-linux, xorg , busybox, dbus, execline, kmod, linux_latest, mdevd, nftables -, s6, s6-linux-init +, s6, s6-linux-init, xdp-tools, spectrum-driver-tools }: let @@ -52,7 +52,7 @@ let }); packages = [ - dbus execline kmod mdevd s6 s6-linux-init s6-rc + dbus execline kmod mdevd s6 s6-linux-init s6-rc xdp-tools (busybox.override { extraConfig = '' @@ -71,7 +71,12 @@ let # Packages that should be fully linked into /usr, # (not just their bin/* files). - usrPackages = [ dbus firmware kernel.modules terminfo ]; + usrPackages = [ + dbus firmware kernel.modules terminfo + + # for xdp-forwarder + spectrum-driver-tools + ]; packagesSysroot = runCommand "packages-sysroot" { inherit packages; diff --git a/vm/sys/net/image/etc/fstab b/vm/sys/net/image/etc/fstab index 5dc9b2a..a0b62fa 100644 --- a/vm/sys/net/image/etc/fstab +++ b/vm/sys/net/image/etc/fstab @@ -1,6 +1,8 @@ # SPDX-License-Identifier: CC0-1.0 # SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is> +# SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> proc /proc proc defaults 0 0 devpts /dev/pts devpts gid=5,mode=620 0 0 tmpfs /dev/shm tmpfs defaults 0 0 sysfs /sys sysfs defaults 0 0 +bpffs /sys/fs/bpf bpf defaults 0 0 diff --git a/vm/sys/net/image/etc/mdev/iface b/vm/sys/net/image/etc/mdev/iface index 2306575..da45ce9 100755 --- a/vm/sys/net/image/etc/mdev/iface +++ b/vm/sys/net/image/etc/mdev/iface @@ -1,36 +1,26 @@ #!/bin/execlineb -P # SPDX-License-Identifier: EUPL-1.2+ # SPDX-FileCopyrightText: 2020-2021 Alyssa Ross <hi@alyssa.is> +# SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> importas -Si INTERFACE ifte { - # This interface is connected to another VM. - - # The other VM's IP is encoded in the NIC-specific portion of the - # interface's MAC address. - backtick -E CLIENT_IP { - awk -F: "{printf \"100.64.%d.%d\\n\", \"0x\" $5, \"0x\" $6}" - /sys/class/net/${INTERFACE}/address - } - - if { ip address add 169.254.0.1/32 dev $INTERFACE } - if { ip link set $INTERFACE up } - ip route add $CLIENT_IP dev $INTERFACE + # This interface is connected to the router + if { xdp-loader load $INTERFACE /usr/lib/xdp/prog_router.o -m skb -p /sys/fs/bpf } + if { ip link set $INTERFACE promisc on } + if { set-router-iface $INTERFACE } + ip link set $INTERFACE up } { if { test $INTERFACE != lo } # This is a physical connection to a network device. - background { s6-rc -bu change connman } - if { s6-rc -bu change nftables } - if { - forx -pE module { nft_counter nft_masq } - modprobe $module - } - nft add rule ip nat postrouting oifname $INTERFACE counter masquerade + if { xdp-loader load $INTERFACE /usr/lib/xdp/prog_physical.o -m skb -p /sys/fs/bpf } + if { ip link set $INTERFACE promisc on } + ip link set $INTERFACE up } grep -iq ^02:01: /sys/class/net/${INTERFACE}/address diff --git a/vm/sys/net/image/etc/nftables.conf b/vm/sys/net/image/etc/nftables.conf index 296d92c..cc8e462 100644 --- a/vm/sys/net/image/etc/nftables.conf +++ b/vm/sys/net/image/etc/nftables.conf @@ -1,8 +1,16 @@ # SPDX-License-Identifier: EUPL-1.2+ -# SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is> +# SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> -table nat { - chain postrouting { - type nat hook postrouting priority 100; +table driver-fw { + chain input { + type filter hook input priority filter; policy drop; + } + + chain output { + type filter hook output priority filter; policy drop; + } + + chain forward { + type filter hook forward priority filter; policy drop; } } -- 2.51.2
This patch has been committed as dcee9414db0977aaccfdb1da6010f82a6044194f, which can be viewed online at https://spectrum-os.org/git/spectrum/commit/?id=dcee9414db0977aaccfdb1da6010.... This is an automated message. Send comments/questions/requests to: Alyssa Ross <hi@alyssa.is>
This allows developers to manually start the iwd daemon and connect to a wireless network, to confirm that the rest of the stack works correctly when used with Wi-Fi. Signed-off-by: Yureka Lilian <yureka@cyberchaos.dev> Reviewed-by: Alyssa Ross <hi@alyssa.is> --- vm/sys/net/default.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/sys/net/default.nix b/vm/sys/net/default.nix index f4e51dd..889e5ec 100644 --- a/vm/sys/net/default.nix +++ b/vm/sys/net/default.nix @@ -8,7 +8,7 @@ pkgsMusl.callPackage ( { lib, stdenvNoCC, nixos, runCommand, writeClosure , erofs-utils, jq, s6-rc, util-linux, xorg , busybox, dbus, execline, kmod, linux_latest, mdevd, nftables -, s6, s6-linux-init, xdp-tools, spectrum-driver-tools +, s6, s6-linux-init, xdp-tools, spectrum-driver-tools, iwd }: let @@ -72,7 +72,7 @@ let # Packages that should be fully linked into /usr, # (not just their bin/* files). usrPackages = [ - dbus firmware kernel.modules terminfo + dbus firmware kernel.modules terminfo iwd # for xdp-forwarder spectrum-driver-tools -- 2.51.2
This patch has been committed as 8b0ae3f449fccfe6da74df9f532ccb93be8ecc6b, which can be viewed online at https://spectrum-os.org/git/spectrum/commit/?id=8b0ae3f449fccfe6da74df9f532c.... This is an automated message. Send comments/questions/requests to: Alyssa Ross <hi@alyssa.is>
The tokio-vhost & vhost-device-net crates which we also wrote and depend on are left external in the outlook of becoming a rust-vmm project soon. Signed-off-by: Yureka Lilian <yureka@cyberchaos.dev> --- pkgs/default.nix | 2 + tools/router/Cargo.lock | 807 ++++++++++++++++++++++++++++++++ tools/router/Cargo.lock.license | 2 + tools/router/Cargo.toml | 21 + tools/router/default.nix | 18 + tools/router/src/main.rs | 73 +++ tools/router/src/packet.rs | 187 ++++++++ tools/router/src/protocol.rs | 65 +++ tools/router/src/router.rs | 147 ++++++ tools/router/src/upstream.rs | 175 +++++++ 10 files changed, 1497 insertions(+) create mode 100644 tools/router/Cargo.lock create mode 100644 tools/router/Cargo.lock.license create mode 100644 tools/router/Cargo.toml create mode 100644 tools/router/default.nix create mode 100644 tools/router/src/main.rs create mode 100644 tools/router/src/packet.rs create mode 100644 tools/router/src/protocol.rs create mode 100644 tools/router/src/router.rs create mode 100644 tools/router/src/upstream.rs diff --git a/pkgs/default.nix b/pkgs/default.nix index 0d159c9..c7311cb 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -52,6 +52,8 @@ let xdg-desktop-portal-spectrum-host = self.callSpectrumPackage ../tools/xdg-desktop-portal-spectrum-host {}; + spectrum-router = self.callSpectrumPackage ../tools/router {}; + # Packages from the overlay, so it's possible to build them from # the CLI easily. inherit (pkgs) cloud-hypervisor dbus; diff --git a/tools/router/Cargo.lock b/tools/router/Cargo.lock new file mode 100644 index 0000000..60d7657 --- /dev/null +++ b/tools/router/Cargo.lock @@ -0,0 +1,807 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "clap" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-macro", + "futures-sink", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "jiff" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "mio" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "spectrum-router" +version = "0.1.0" +dependencies = [ + "anyhow", + "arrayvec", + "clap", + "env_logger", + "futures-util", + "log", + "tokio", + "tokio-stream", + "tokio-util", + "vhost-device-net", + "vm-memory", + "zerocopy", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-eventfd" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e66bd133670ac39baa1aca5c3a86709f4595c08ca4464a1e1400b83d62c0639" +dependencies = [ + "futures-lite", + "libc", + "tokio", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-vhost" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e218319ac446f670a0e049050e22c3dd3bdd5cb34a6e91e90a95a48c0a76fe7" +dependencies = [ + "async-stream", + "bitvec", + "futures-util", + "libc", + "log", + "tokio", + "tokio-eventfd", + "virtio-queue", + "vm-memory", + "zerocopy", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "vhost-device-net" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf2581adf4a04f8361e92208b6fb81aa8d82b7f0a2801a4621153919db085e6" +dependencies = [ + "futures-util", + "log", + "tokio", + "tokio-vhost", + "vm-memory", +] + +[[package]] +name = "virtio-bindings" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "804f498a26d5a63be7bbb8bdcd3869c3f286c4c4a17108905276454da0caf8cb" + +[[package]] +name = "virtio-queue" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb0479158f863e59323771a1f684d843962f76960b86fecfec2bfa9c8f0f9180" +dependencies = [ + "log", + "virtio-bindings", + "vm-memory", + "vmm-sys-util", +] + +[[package]] +name = "vm-memory" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd5e56d48353c5f54ef50bd158a0452fc82f5383da840f7b8efc31695dd3b9d" +dependencies = [ + "libc", + "thiserror", + "winapi", +] + +[[package]] +name = "vmm-sys-util" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d21f366bf22bfba3e868349978766a965cbe628c323d58e026be80b8357ab789" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "zerocopy" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/tools/router/Cargo.lock.license b/tools/router/Cargo.lock.license new file mode 100644 index 0000000..9b37611 --- /dev/null +++ b/tools/router/Cargo.lock.license @@ -0,0 +1,2 @@ +SPDX-License-Identifier: CC0-1.0 +SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> diff --git a/tools/router/Cargo.toml b/tools/router/Cargo.toml new file mode 100644 index 0000000..0b96911 --- /dev/null +++ b/tools/router/Cargo.toml @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: CC0-1.0 +# SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> + +[package] +name = "spectrum-router" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow = "1.0.100" +clap = { version = "4.5.45", features = ["derive"] } +env_logger = "0.11.8" +log = { version = "0.4.27", features = ["release_max_level_debug"] } +vhost-device-net = "0.1.0" +tokio = { version = "1.48.0", features = ["macros", "rt"] } +futures-util = "0.3.31" +zerocopy = "0.8.27" +tokio-stream = "0.1.17" +arrayvec = "0.7.6" +vm-memory = "0.16" +tokio-util = "0.7.17" diff --git a/tools/router/default.nix b/tools/router/default.nix new file mode 100644 index 0000000..e70f9ec --- /dev/null +++ b/tools/router/default.nix @@ -0,0 +1,18 @@ +# SPDX-FileCopyrightText: 2024 Alyssa Ross <hi@alyssa.is> +# SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> +# SPDX-License-Identifier: MIT + +import ../../lib/call-package.nix ( +{ src, lib, rustPlatform }: + +rustPlatform.buildRustPackage { + name = "spectrum-router"; + + src = lib.fileset.toSource { + root = ../..; + fileset = lib.fileset.intersection src ./.; + }; + sourceRoot = "source/tools/router"; + + cargoLock.lockFile = ./Cargo.lock; +}) (_: {}) diff --git a/tools/router/src/main.rs b/tools/router/src/main.rs new file mode 100644 index 0000000..e3aca65 --- /dev/null +++ b/tools/router/src/main.rs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: EUPL-1.2+ +// SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> + +pub(crate) mod packet; +pub(crate) mod protocol; +mod router; +mod upstream; + +use std::path::PathBuf; + +use packet::*; +use router::{InterfaceId, Router}; +use upstream::Upstream; + +use clap::Parser; +use futures_util::{SinkExt, TryStreamExt}; +use log::{error, info}; +use tokio::net::UnixListener; +use vhost_device_net::{IncomingPacket, VhostDeviceNet}; +use vm_memory::GuestMemoryMmap; + +#[derive(Parser, Debug)] +#[command()] //version = None, about = None, long_about = None)] +struct Args { + #[arg(long)] + driver_listen_path: PathBuf, + #[arg(long)] + app_listen_path: PathBuf, +} + +fn main() -> anyhow::Result<()> { + env_logger::init(); + let args = Args::parse(); + + for path in [&args.driver_listen_path, &args.app_listen_path] { + let _ = std::fs::remove_file(path); + } + + run_router(args) +} +#[tokio::main(flavor = "current_thread")] +async fn run_router(args: Args) -> anyhow::Result<()> { + let app_listener = UnixListener::bind(&args.app_listen_path)?; + let driver_listener = UnixListener::bind(&args.driver_listen_path)?; + + let mut router = Router::<GuestMemoryMmap>::new(InterfaceId::Upstream); + + let (mut upstream, upstream_tx, upstream_rx) = Upstream::new(driver_listener); + router.add_iface(InterfaceId::Upstream, upstream_tx, upstream_rx); + + tokio::spawn(async move { upstream.run().await }); + + let mut app_num = 0; + + loop { + tokio::select! { + app_conn = app_listener.accept() => { + info!("app connected"); + match app_conn { + Ok((stream, _addr)) => { + let device = VhostDeviceNet::from_unix_stream(stream).await?; + let stream = Box::pin(device.tx().await?.map_ok(|buf| Packet::Incoming { buf: Some(buf), decap_vlan: false })); + let sink = Box::pin(device.rx().await?.with(|packet: Packet<IncomingPacket<GuestMemoryMmap>>| async move { Ok(packet.out(None)?.into_reader()) })); + router.add_iface(InterfaceId::App(app_num), stream, sink); + app_num = app_num.checked_add(1).unwrap(); + } + Err(e) => error!("app connection failed: {}", e), + } + } + _ = router.run() => {} + } + } +} diff --git a/tools/router/src/packet.rs b/tools/router/src/packet.rs new file mode 100644 index 0000000..1f14ef1 --- /dev/null +++ b/tools/router/src/packet.rs @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: EUPL-1.2+ +// SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> + +use std::io::{self, Chain, Cursor, Read}; + +use crate::protocol::*; + +use arrayvec::ArrayVec; +use zerocopy::*; + +pub enum PacketData<R> { + Incoming(R), + Bytes(Cursor<Box<[u8]>>), +} + +impl<R: Read> Read for PacketData<R> { + fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { + match self { + PacketData::Incoming(r) => r.read(buf), + PacketData::Bytes(b) => b.read(buf), + } + } +} + +impl<R: Read> PacketData<R> { + pub fn full_packet(&mut self) -> &[u8] { + match self { + PacketData::Bytes(b) => b.get_ref().as_ref(), + PacketData::Incoming(r) => { + let mut buf = vec![]; + r.read_to_end(&mut buf).unwrap(); + *self = PacketData::Bytes(Cursor::new(buf.into_boxed_slice())); + let PacketData::Bytes(b) = self else { + unreachable!() + }; + b.get_ref().as_ref() + } + } + } +} + +pub enum Packet<R> { + /// The packet has not been looked at / read into our memory yet + Incoming { decap_vlan: bool, buf: Option<R> }, + /// We've read the head of the packet to look at the headers. + Peek { + decap_vlan: bool, + peek: ArrayVec<u8, 64>, + buf: PacketData<R>, + }, +} + +pub struct PacketHeaders<'a, R> { + pub ether_frame: &'a mut EtherFrame, + pub vlan_tag: Option<&'a mut VlanTag>, + pub ether_type: &'a mut EtherType, + pub ipv6_hdr: Option<&'a mut Ipv6Header>, + pub peek_slice: &'a mut [u8], + pub buf: &'a mut PacketData<R>, +} + +impl<R: Read> Packet<R> { + fn peek( + &mut self, + ) -> ( + &mut ArrayVec<u8, 64>, + &mut PacketData<R>, + &mut bool, // decap_vlan + ) { + match self { + Packet::Incoming { buf, decap_vlan } => { + let mut buf = std::mem::take(buf).unwrap(); + // A stack allocation which can keep all headers we are interested in + let mut peek = [0u8; 64]; + // Read the first 64 bytes + // 64 >= 14 (ether) + 4 (vlan) + 40 (ipv6) + 4 (icmpv6) + let n = buf.read(&mut peek).unwrap(); + + let buf = PacketData::Incoming(buf); + let mut peek = ArrayVec::from(peek); + peek.truncate(n); + *self = Packet::Peek { + peek, + buf, + decap_vlan: *decap_vlan, + }; + let Packet::Peek { + peek, + buf, + decap_vlan, + } = self + else { + unreachable!() + }; + (peek, buf, decap_vlan) + } + Packet::Peek { + peek, + buf, + decap_vlan, + } => (peek, buf, decap_vlan), + } + } + pub fn headers(&mut self) -> io::Result<PacketHeaders<'_, R>> { + let (peek, buf, decap_vlan) = self.peek(); + let peek_slice = peek.as_mut_slice(); + let (ether_frame, peek_slice) = EtherFrame::mut_from_prefix(peek_slice) + .map_err(|_| io::Error::other("packet with <12 bytes"))?; + let (ether_type, _) = EtherType::ref_from_prefix(peek_slice) + .map_err(|_| io::Error::other("packet with <14 bytes"))?; + + let (vlan_tag, peek_slice) = if *decap_vlan && *ether_type == ETHER_TYPE_802_1Q { + let (vlan, peek_slice) = VlanTag::mut_from_prefix(peek_slice) + .map_err(|_| io::Error::other("packet with <16 bytes"))?; + (Some(vlan), peek_slice) + } else { + (None, peek_slice) + }; + let (ether_type, peek_slice) = EtherType::mut_from_prefix(peek_slice) + .map_err(|_| io::Error::other("packet with <18 bytes"))?; + + let (ipv6_hdr, peek_slice) = if *ether_type == ETHER_TYPE_IPV6 { + let (ipv6_hdr, peek_slice) = Ipv6Header::mut_from_prefix(peek_slice) + .map_err(|_| io::Error::other("short ipv6 header"))?; + (Some(ipv6_hdr), peek_slice) + } else { + (None, peek_slice) + }; + + Ok(PacketHeaders { + ether_frame, + vlan_tag, + ether_type, + ipv6_hdr, + peek_slice, + buf, + }) + } + pub fn out(mut self, vlan_encap: Option<VlanTag>) -> io::Result<OutgoingPacket<R>> { + let PacketHeaders { + ether_frame, + ether_type, + ipv6_hdr, + peek_slice, + .. + } = self.headers()?; + + let mut headers_out = ArrayVec::<u8, 128>::new(); + headers_out + .try_extend_from_slice(ether_frame.as_bytes()) + .unwrap(); + if let Some(vlan_tag) = vlan_encap { + headers_out + .try_extend_from_slice(vlan_tag.as_bytes()) + .unwrap(); + } + headers_out + .try_extend_from_slice(ether_type.as_bytes()) + .unwrap(); + if let Some(ipv6_hdr) = ipv6_hdr { + headers_out + .try_extend_from_slice(ipv6_hdr.as_bytes()) + .unwrap(); + } + headers_out.try_extend_from_slice(peek_slice).unwrap(); + + let Packet::Peek { + peek: _peek, buf, .. + } = self + else { + unreachable!() + }; + Ok(OutgoingPacket { headers_out, buf }) + } +} + +pub struct OutgoingPacket<R> { + /// This has extra space for added encapsulation / VLAN tags + headers_out: ArrayVec<u8, 128>, + buf: PacketData<R>, +} + +impl<R: Read> OutgoingPacket<R> { + pub fn into_reader(self) -> Chain<Cursor<ArrayVec<u8, 128>>, PacketData<R>> { + Cursor::new(self.headers_out).chain(self.buf) + } +} diff --git a/tools/router/src/protocol.rs b/tools/router/src/protocol.rs new file mode 100644 index 0000000..341b7ff --- /dev/null +++ b/tools/router/src/protocol.rs @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: EUPL-1.2+ +// SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> + +use zerocopy::byteorder::network_endian::{U16, U32}; +use zerocopy::*; + +pub const ETHER_TYPE_IPV6: u16 = 0x86dd; +pub const ETHER_TYPE_802_1Q: u16 = 0x8100; +pub const IP_PROTO_ICMP6: u8 = 0x3a; +pub const ICMP6_TYPE_R_ADV: u8 = 134; + +pub type MacAddr = [u8; 6]; +pub fn is_multicast(mac: &MacAddr) -> bool { + match mac { + [0xff, 0xff, 0xff, 0xff, 0xff, 0xff] => true, + [0x01, 0x80, 0xc2, _, _, _] => true, // 802 group + [0x33, 0x33, _, _, _, _] => true, // IPv6 multicast + _ => false, + } +} + +#[derive(Debug, PartialEq, Eq, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)] +#[repr(C)] +pub struct EtherFrame { + pub dst_addr: MacAddr, + pub src_addr: MacAddr, +} + +pub type EtherType = U16; + +#[derive(Debug, PartialEq, Eq, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)] +#[repr(C)] +pub struct VlanTag { + pub ether_type: U16, + pub tag_control_information: U16, +} + +#[derive(Debug, PartialEq, Eq, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)] +#[repr(C)] +pub struct Ipv6Header { + pub version_traffic_class_flow_label: U32, + pub payload_length: U16, + pub next_header: u8, + pub hop_limit: u8, + pub src_addr: [u8; 16], + pub dst_addr: [u8; 16], +} + +#[derive(Debug, PartialEq, Eq, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)] +#[repr(C)] +pub struct Icmpv6Header { + pub msg_type: u8, + pub code: u8, + pub checksum: U16, +} + +#[derive(Debug, PartialEq, Eq, FromBytes, IntoBytes, KnownLayout, Immutable, Unaligned)] +#[repr(C)] +pub struct Icmpv6RouterAdvertisement { + pub hop_limit: u8, + pub flags: u8, + pub router_lifetime: U16, + pub reachable_time: U32, + pub retrans_timer: U32, +} diff --git a/tools/router/src/router.rs b/tools/router/src/router.rs new file mode 100644 index 0000000..a91e415 --- /dev/null +++ b/tools/router/src/router.rs @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: EUPL-1.2+ +// SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> + +use std::collections::HashMap; +use std::io::{self, Cursor}; +use std::net::Ipv6Addr; +use std::pin::Pin; +use std::time::Duration; + +use crate::packet::*; +use crate::protocol::*; + +use futures_util::{FutureExt, Sink, SinkExt, Stream, StreamExt}; +use log::{debug, info, warn}; +use tokio_stream::StreamMap; +use vhost_device_net::IncomingPacket; +use vm_memory::GuestMemory; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum InterfaceId { + Upstream, + App(usize), + Broadcast, +} + +pub type PacketStream<M> = Pin<Box<dyn Stream<Item = io::Result<Packet<IncomingPacket<M>>>>>>; +pub type PacketSink<M> = Pin<Box<dyn Sink<Packet<IncomingPacket<M>>, Error = io::Error>>>; + +pub struct Router<M: GuestMemory> { + streams: StreamMap<InterfaceId, PacketStream<M>>, + sinks: HashMap<InterfaceId, PacketSink<M>>, + fib: HashMap<Ipv6Addr, (MacAddr, InterfaceId)>, + default_out: InterfaceId, +} + +impl<M: GuestMemory> Router<M> { + pub fn new(default_out: InterfaceId) -> Self { + Self { + streams: Default::default(), + sinks: Default::default(), + fib: Default::default(), + default_out, + } + } + + pub fn add_iface(&mut self, id: InterfaceId, stream: PacketStream<M>, sink: PacketSink<M>) { + self.streams.insert(id.clone(), stream); + self.sinks.insert(id.clone(), sink); + } + + pub async fn run(&mut self) -> io::Result<()> { + loop { + let next_res = self.streams.next().await; + let Some((in_iface, Ok(mut packet))) = next_res else { + info!("incoming err"); + continue; + }; + + let PacketHeaders { + ether_frame, + ipv6_hdr, + .. + } = packet.headers()?; + + let Some(ipv6_hdr) = ipv6_hdr else { + continue; + }; + let src_addr = Ipv6Addr::from(ipv6_hdr.src_addr); + let dst_addr = Ipv6Addr::from(ipv6_hdr.dst_addr); + + let out_iface = if is_multicast(ðer_frame.dst_addr) { + InterfaceId::Broadcast + } else if let Some((dst_mac, if_idx)) = self.fib.get(&dst_addr) { + ether_frame.dst_addr = *dst_mac; + if_idx.clone() + } else if in_iface != self.default_out { + self.default_out.clone() + } else { + warn!("no fib match for {}, dropping packet", dst_addr); + continue; + }; + + if in_iface != self.default_out + && !src_addr.is_unspecified() + && !src_addr.is_multicast() + && !self.fib.contains_key(&src_addr) + { + debug!( + "adding fib entry for {} -> {:x?} {:?}", + src_addr, ether_frame.src_addr, in_iface + ); + self.fib + .insert(src_addr, (ether_frame.src_addr, in_iface.clone())); + } + + match out_iface { + InterfaceId::Broadcast => { + let Packet::Peek { + peek, + mut buf, + decap_vlan, + } = packet + else { + unreachable!() + }; + let buf = Box::<[u8]>::from(buf.full_packet()); + futures_util::future::try_join_all( + self.sinks + .iter_mut() + .filter(|(id, _)| **id != in_iface) + .map(|(id, sink)| { + let packet = Packet::Peek { + peek: peek.clone(), + buf: PacketData::Bytes(Cursor::new(buf.clone())), + decap_vlan, + }; + let fut = sink.send(packet); + tokio::time::timeout(Duration::from_secs(1), fut).map(move |res| match res { + Err(_) => { + warn!("interface {:?} has been blocked for 1 sec, dropping packet", id); + Ok(()) + }, + Ok(Err(e)) => Err(e), + Ok(Ok(())) => Ok(()), + }) + }), + ) + .await?; + } + ref unicast => { + let Some(sink) = self.sinks.get_mut(unicast) else { + warn!("dropped packet because interface is not ready"); + continue; + }; + match tokio::time::timeout(Duration::from_secs(1), sink.send(packet)).await { + Err(_) => warn!( + "interface {:?} has been blocked for 1 sec, dropping packet", + unicast + ), + Ok(Err(e)) => return Err(e), + Ok(Ok(())) => {} + } + } + } + } + } +} diff --git a/tools/router/src/upstream.rs b/tools/router/src/upstream.rs new file mode 100644 index 0000000..08229e8 --- /dev/null +++ b/tools/router/src/upstream.rs @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: EUPL-1.2+ +// SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> + +use std::io::{self, Cursor, Read}; +use std::pin::Pin; +use std::time::{Duration, Instant}; + +use crate::packet::*; +use crate::protocol::*; +use crate::router::{PacketSink, PacketStream}; + +use futures_util::{Sink, SinkExt, Stream, StreamExt}; +use log::{debug, error, info, warn}; +use tokio::net::UnixListener; +use tokio::sync::mpsc; +use tokio_stream::wrappers::ReceiverStream; +use tokio_util::sync::PollSender; +use vhost_device_net::{IncomingPacket, VhostDeviceNet}; +use vm_memory::GuestMemoryMmap; +use zerocopy::FromBytes; + +pub struct Upstream { + driver_listener: UnixListener, + active_interface: Option<u16>, + reevaluate_active_interface: Pin<Box<tokio::time::Sleep>>, + radv_valid_until: Vec<(u16, Instant)>, + tx_sender: mpsc::Sender<Packet<IncomingPacket<GuestMemoryMmap>>>, + rx_receiver: mpsc::Receiver<Packet<IncomingPacket<GuestMemoryMmap>>>, +} + +impl Upstream { + pub fn new( + driver_listener: UnixListener, + ) -> ( + Upstream, + PacketStream<GuestMemoryMmap>, + PacketSink<GuestMemoryMmap>, + ) { + let (tx_sender, tx_receiver) = mpsc::channel(64); + let (rx_sender, rx_receiver) = mpsc::channel(64); + + ( + Upstream { + driver_listener, + active_interface: None, + reevaluate_active_interface: Box::pin(tokio::time::sleep(Duration::from_hours( + 24 * 365, + ))), + radv_valid_until: Default::default(), + tx_sender, + rx_receiver, + }, + Box::pin(ReceiverStream::new(tx_receiver).map(Ok)), + Box::pin( + PollSender::new(rx_sender) + .sink_map_err(|_| io::Error::other("driver rx channel closed")), + ), + ) + } + pub async fn run(&mut self) -> io::Result<()> { + let mut device_tx: Option<Pin<Box<dyn Stream<Item = _> + Send>>> = None; + let mut device_rx: Option<Pin<Box<dyn Sink<_, Error = _> + Send>>> = None; + loop { + tokio::select! { + driver_conn = self.driver_listener.accept() => { + info!("driver connected"); + match driver_conn { + Ok((stream, _addr)) => { + self.radv_valid_until.clear(); + self.active_interface = None; + self.reevaluate_active_interface.as_mut().reset((Instant::now() + Duration::from_hours(24 * 365)).into()); + + let device = VhostDeviceNet::from_unix_stream(stream).await?; + device_tx = Some(Box::pin(device.tx().await?)); + device_rx = Some(Box::pin(device.rx().await?)); + } + Err(e) => error!("driver connection failed: {}", e), + } + } + tx_res = async { device_tx.as_mut().unwrap().next().await }, if device_tx.is_some() => { + let Some(Ok(buf)) = tx_res else { + info!("driver tx err"); + continue; + }; + + let mut packet = Packet::Incoming { buf: Some(buf), decap_vlan: true }; + let PacketHeaders { ether_frame, vlan_tag: vlan_in, ipv6_hdr, peek_slice, buf, .. } = packet.headers()?; + + let Some(vlan_tag) = vlan_in else { + warn!("untagged packet from driver"); + continue; + }; + + let vlan_id = u16::from(vlan_tag.tag_control_information) & 0xfff; + + if let Some(ref ipv6_hdr) = ipv6_hdr && ipv6_hdr.next_header == IP_PROTO_ICMP6 { + let (icmpv6_hdr, icmpv6_data) = Icmpv6Header::ref_from_prefix(peek_slice).map_err(|_| io::Error::other("short icmpv6 header"))?; + + if icmpv6_hdr.msg_type == ICMP6_TYPE_R_ADV { + let data = Cursor::new(icmpv6_data).chain(Cursor::new(buf.full_packet())); + let r_adv = Icmpv6RouterAdvertisement::read_from_io(data)?; + if r_adv.router_lifetime != 0 { + let now = Instant::now(); + let r_adv_timeout = now + Duration::from_secs(u16::from(r_adv.router_lifetime).into()); + match self.radv_valid_until.binary_search_by_key(&vlan_id, |&(if_idx, _)| if_idx) { + Ok(pos) => self.radv_valid_until[pos] = (vlan_id, r_adv_timeout), + Err(insert_pos) => self.radv_valid_until.insert(insert_pos, (vlan_id, r_adv_timeout)), + }; + debug!("router advertisement received on interface {}: {:x?} {:x?} {:?}", vlan_id, ether_frame, ipv6_hdr, r_adv); + + let prev_active_interface = self.active_interface.unwrap_or(u16::MAX); + if vlan_id < prev_active_interface || self.reevaluate_active_interface.deadline() < now.into() { + self.active_interface = Some(vlan_id); + info!("set active interface to {}", vlan_id); + self.reevaluate_active_interface.as_mut().reset(r_adv_timeout.into()); + } else if vlan_id == prev_active_interface { + self.reevaluate_active_interface.as_mut().reset(r_adv_timeout.into()); + } + } + } + } + + if Some(vlan_id) != self.active_interface { + debug!("dropping packet from inactive interface {}", vlan_id); + continue; + } + + self.tx_sender.send(packet).await.map_err(io::Error::other)?; + } + rx_res = self.rx_receiver.recv() => { + let Some(packet) = rx_res else { + info!("driver rx err"); + continue; + }; + + let Some(sink) = device_rx.as_mut() else { + warn!("dropped packet because driver is not ready"); + continue; + }; + + let Some(active_interface) = &self.active_interface else { + warn!("dropped packet because active interface is unknown"); + continue; + }; + + // Add active interface vlan + let vlan_out = VlanTag { + ether_type: ETHER_TYPE_802_1Q.into(), + tag_control_information: (*active_interface).into(), + }; + + let packet = packet.out(Some(vlan_out))?; + + match tokio::time::timeout(Duration::from_secs(1), sink.send(packet.into_reader())).await { + Err(_) => warn!("driver rx has been blocked for 1 sec, dropping packet"), + Ok(Err(e)) => return Err(e), + Ok(Ok(())) => {}, + } + } + () = &mut self.reevaluate_active_interface => { + let now = Instant::now(); + let prev_active_interface = self.active_interface.unwrap_or(u16::MAX); + info!("router advertisement expired on interface {}", prev_active_interface); + if let Some((if_idx, valid_until)) = self.radv_valid_until.iter().find(|(_, valid_until)| *valid_until > now) { + self.active_interface = Some(*if_idx); + info!("set active interface to {}", if_idx); + self.reevaluate_active_interface.as_mut().reset((*valid_until).into()); + } else { + self.reevaluate_active_interface.as_mut().reset((now + Duration::from_hours(24 * 365)).into()); + } + } + } + } + } +} -- 2.51.2
This patch has been committed as 5e69c680f678c12d7cc2ed5197788fc5e2367fc7, which can be viewed online at https://spectrum-os.org/git/spectrum/commit/?id=5e69c680f678c12d7cc2ed519778.... This is an automated message. Send comments/questions/requests to: Alyssa Ross <hi@alyssa.is>
This removes the old host bridge + taps glue, and instead connects the apps to their net provider's router instance. Signed-off-by: Yureka Lilian <yureka@cyberchaos.dev> --- host/rootfs/default.nix | 4 +- host/rootfs/file-list.mk | 2 + .../data/service/spectrum-router/down | 0 .../template/data/service/spectrum-router/run | 9 +++ host/rootfs/image/usr/bin/run-vmm | 21 +++--- host/rootfs/image/usr/bin/vm-import | 13 ---- tools/start-vmm/ch.rs | 66 +---------------- tools/start-vmm/lib.rs | 73 ++++++++++++------- tools/start-vmm/meson.build | 9 +-- tools/start-vmm/net-util.c | 39 ---------- tools/start-vmm/net-util.h | 6 -- tools/start-vmm/net.c | 55 -------------- tools/start-vmm/net.rs | 11 --- tools/start-vmm/tests/meson.build | 5 -- .../start-vmm/tests/tap_open-name-too-long.c | 20 ----- tools/start-vmm/tests/tap_open.c | 28 ------- 16 files changed, 77 insertions(+), 284 deletions(-) create mode 100644 host/rootfs/image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/spectrum-router/down create mode 100755 host/rootfs/image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/spectrum-router/run delete mode 100644 tools/start-vmm/net-util.c delete mode 100644 tools/start-vmm/net-util.h delete mode 100644 tools/start-vmm/net.c delete mode 100644 tools/start-vmm/tests/tap_open-name-too-long.c delete mode 100644 tools/start-vmm/tests/tap_open.c diff --git a/host/rootfs/default.nix b/host/rootfs/default.nix index b441a51..a759568 100644 --- a/host/rootfs/default.nix +++ b/host/rootfs/default.nix @@ -8,7 +8,7 @@ import ../../lib/call-package.nix ( }: pkgsMusl.callPackage ( -{ spectrum-host-tools +{ spectrum-host-tools, spectrum-router , lib, stdenvNoCC, nixos, runCommand, writeClosure, erofs-utils, s6-rc , busybox, cloud-hypervisor, cosmic-files, crosvm, cryptsetup , dejavu_fonts, dbus, execline, foot, fuse3, iproute2, inotify-tools @@ -27,7 +27,7 @@ let packages = [ btrfs-progs cloud-hypervisor cosmic-files crosvm cryptsetup dbus execline fuse3 inotify-tools iproute2 jq kmod mdevd s6 s6-linux-init - s6-rc socat spectrum-host-tools util-linuxMinimal virtiofsd + s6-rc socat spectrum-host-tools spectrum-router util-linuxMinimal virtiofsd xdg-desktop-portal-spectrum-host (foot.override { allowPgo = false; }) diff --git a/host/rootfs/file-list.mk b/host/rootfs/file-list.mk index 56f693e..bfe3940 100644 --- a/host/rootfs/file-list.mk +++ b/host/rootfs/file-list.mk @@ -27,6 +27,8 @@ FILES = \ image/etc/s6-linux-init/run-image/service/vm-services/run \ image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/dbus/notification-fd \ image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/dbus/run \ + image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/spectrum-router/down \ + image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/spectrum-router/run \ image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/vhost-user-fs/notification-fd \ image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/vhost-user-fs/run \ image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/vhost-user-gpu/notification-fd \ diff --git a/host/rootfs/image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/spectrum-router/down b/host/rootfs/image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/spectrum-router/down new file mode 100644 index 0000000..e69de29 diff --git a/host/rootfs/image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/spectrum-router/run b/host/rootfs/image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/spectrum-router/run new file mode 100755 index 0000000..7b3e3db --- /dev/null +++ b/host/rootfs/image/etc/s6-linux-init/run-image/service/vm-services/template/data/service/spectrum-router/run @@ -0,0 +1,9 @@ +#!/bin/execlineb -P +# SPDX-License-Identifier: EUPL-1.2+ +# SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> + +importas -i VM VM + +export RUST_LOG spectrum-router=debug,info +spectrum-router --app-listen-path ${VM}/router-app.sock --driver-listen-path ${VM}/router-driver.sock + diff --git a/host/rootfs/image/usr/bin/run-vmm b/host/rootfs/image/usr/bin/run-vmm index 5649674..ba8b59c 100755 --- a/host/rootfs/image/usr/bin/run-vmm +++ b/host/rootfs/image/usr/bin/run-vmm @@ -20,6 +20,7 @@ background -d { assign-devices } + # Find any net-provider relationships this VM is involved in cd /run/vm/by-id elglob -0 providers */config/providers/net/* forx -pE path { $providers } @@ -31,23 +32,21 @@ background -d { backtick -E link_path { readlink /run/vm/by-name/${router} } basename -- $link_path } - + # This VM may be either the driver or the client if -n { if { test $client_id != $1 } test $router_id != $1 } - backtick -E mac { - pipeline { ip -j link show client-${client_id} } - pipeline { jq -r ".[].ifindex" } - awk "{ - printf \"02:01:%02X:%02X:%02X:%02X\", $0 / 256 ^ 3 % 256, - $0 / 256 ^ 2 % 256, $0 / 256 % 256, $0 % 256 - }" + if { + s6-svc -U /run/service/vm-services/instance/${router_id}/data/service/spectrum-router } - - ch-remote --api-socket /run/vm/by-id/${router_id}/vmm add-net - id=router-${client_id},tap=router-${client_id},mac=${mac} + if { + s6-svwait -U /run/service/vmm/instance/${router_id} + } + # Adding the interface is re-entrant and may be called multiple times. Thus, accept failures. + redirfd -w 2 /dev/null + ch-remote --api-socket /run/vm/by-id/${router_id}/vmm add-net id=router,vhost_user=on,socket=/run/vm/by-id/${router_id}/router-driver.sock,mac=02:01:00:00:00:01 } unexport ! fdmove -c 3 0 diff --git a/host/rootfs/image/usr/bin/vm-import b/host/rootfs/image/usr/bin/vm-import index de88f08..c1d1bbc 100755 --- a/host/rootfs/image/usr/bin/vm-import +++ b/host/rootfs/image/usr/bin/vm-import @@ -14,19 +14,6 @@ if { ln -s -- ${dir} /run/vm/by-name/${1}.${name} } if { ln -s -- ${2}/${name} ${dir}/config } if { ln -s -- /run/service/vmm/instance/${id} ${dir}/service } -if { - if -t { elglob -0d " " providers ${name}/providers/net test -n $providers } - - if { ip link add br-${id} type bridge } - if { ip link set br-${id} up } - - if { ip tuntap add client-${id} mode tap } - if { ip link set client-${id} master br-${id} up } - - if { ip tuntap add router-${id} mode tap } - ip link set router-${id} master br-${id} up -} - if { create-vm-dependencies $id } s6-instance-create -- /run/service/vmm $id diff --git a/tools/start-vmm/ch.rs b/tools/start-vmm/ch.rs index abe1742..4764dd5 100644 --- a/tools/start-vmm/ch.rs +++ b/tools/start-vmm/ch.rs @@ -1,26 +1,19 @@ // SPDX-License-Identifier: EUPL-1.2+ // SPDX-FileCopyrightText: 2022-2024 Alyssa Ross <hi@alyssa.is> +// SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> -use std::convert::TryFrom; use std::ffi::OsStr; use std::fs::File; use std::io::Write; -use std::mem::take; -use std::num::NonZeroI32; use std::os::unix::prelude::*; use std::path::Path; use std::process::{Command, Stdio}; -use std::string::FromUtf8Error; use miniserde::{Serialize, json}; use crate::net::MacAddress; use crate::s6::notify_readiness; -// Trivially safe. -const EPERM: NonZeroI32 = NonZeroI32::new(1).unwrap(); -const EPROTO: NonZeroI32 = NonZeroI32::new(71).unwrap(); - #[derive(Serialize)] pub struct ConsoleConfig { pub mode: &'static str, @@ -46,7 +39,8 @@ pub struct GpuConfig { #[derive(Serialize)] pub struct NetConfig { - pub fd: RawFd, + pub vhost_user: bool, + pub vhost_socket: String, pub id: String, pub mac: MacAddress, } @@ -99,11 +93,7 @@ fn command(vm_dir: &Path, s: impl AsRef<OsStr>) -> Command { command } -pub fn create_vm(vm_dir: &Path, ready_fd: File, mut config: VmConfig) -> Result<(), String> { - // Net devices can't be created from file descriptors in vm.create. - // https://github.com/cloud-hypervisor/cloud-hypervisor/issues/5523 - let nets = take(&mut config.net); - +pub fn create_vm(vm_dir: &Path, ready_fd: File, config: VmConfig) -> Result<(), String> { let mut ch_remote = command(vm_dir, "create") .args(["--", "-"]) .stdin(Stdio::piped()) @@ -128,53 +118,5 @@ pub fn create_vm(vm_dir: &Path, ready_fd: File, mut config: VmConfig) -> Result< notify_readiness(ready_fd)?; - for net in nets { - add_net(vm_dir, &net).map_err(|e| format!("failed to add net: {e}"))?; - } - Ok(()) } - -pub fn add_net(vm_dir: &Path, net: &NetConfig) -> Result<(), NonZeroI32> { - let mut ch_remote = command(vm_dir, "add-net") - .arg(format!("fd={},id={},mac={}", net.fd, net.id, net.mac)) - .stdout(Stdio::piped()) - .spawn() - .or(Err(EPERM))?; - - if let Ok(ch_remote_status) = ch_remote.wait() - && ch_remote_status.success() - { - return Ok(()); - } - - Err(EPROTO) -} - -#[repr(C)] -pub struct NetConfigC { - pub fd: RawFd, - pub id: [u8; 18], - pub mac: MacAddress, -} - -impl<'a> TryFrom<&'a NetConfigC> for NetConfig { - type Error = FromUtf8Error; - - fn try_from(c: &'a NetConfigC) -> Result<NetConfig, Self::Error> { - let nul_index = c.id.iter().position(|&c| c == 0).unwrap_or(c.id.len()); - Ok(NetConfig { - fd: c.fd, - id: String::from_utf8(c.id[..nul_index].to_vec())?, - mac: c.mac, - }) - } -} - -impl TryFrom<NetConfigC> for NetConfig { - type Error = FromUtf8Error; - - fn try_from(c: NetConfigC) -> Result<NetConfig, Self::Error> { - Self::try_from(&c) - } -} diff --git a/tools/start-vmm/lib.rs b/tools/start-vmm/lib.rs index 0422d85..b44e037 100644 --- a/tools/start-vmm/lib.rs +++ b/tools/start-vmm/lib.rs @@ -1,23 +1,24 @@ // SPDX-License-Identifier: EUPL-1.2+ // SPDX-FileCopyrightText: 2022-2024 Alyssa Ross <hi@alyssa.is> +// SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> mod ch; mod net; mod s6; use std::borrow::Cow; -use std::convert::TryInto; use std::env::args_os; use std::ffi::OsStr; use std::fs::File; -use std::io::{self, ErrorKind}; +use std::hash::{Hash, Hasher}; +use std::io::ErrorKind; use std::path::Path; use ch::{ - ConsoleConfig, DiskConfig, FsConfig, GpuConfig, LandlockConfig, MemoryConfig, PayloadConfig, - VmConfig, VsockConfig, + ConsoleConfig, DiskConfig, FsConfig, GpuConfig, LandlockConfig, MemoryConfig, NetConfig, + PayloadConfig, VmConfig, VsockConfig, }; -use net::net_setup; +use net::MacAddress; pub fn prog_name() -> String { args_os() @@ -40,8 +41,6 @@ pub fn vm_config(vm_dir: &Path) -> Result<VmConfig, String> { return Err(format!("VM name may not contain a colon: {vm_name:?}")); } - let name_bytes = vm_name.as_bytes(); - let config_dir = vm_dir.join("config"); let blk_dir = config_dir.join("blk"); let kernel_path = config_dir.join("vmlinux"); @@ -97,24 +96,48 @@ pub fn vm_config(vm_dir: &Path) -> Result<VmConfig, String> { shared: true, }, net: match net_providers_dir.read_dir() { - Ok(_) => { - // SAFETY: we check the result. - let net = unsafe { - net_setup( - name_bytes.as_ptr().cast(), - name_bytes - .len() - .try_into() - .map_err(|e| format!("VM name too long: {e}"))?, - ) - }; - if net.fd == -1 { - let e = io::Error::last_os_error(); - return Err(format!("setting up networking failed: {e}")); - } - - vec![net.try_into().unwrap()] - } + Ok(entries) => entries + .into_iter() + .map(|result| { + Ok(result + .map_err(|e| format!("examining directory entry: {e}"))? + .path()) + }) + .map(|result: Result<_, String>| { + let provider_name = result? + .file_name() + .ok_or("unable to get net provider name".to_string())? + .to_str() + .unwrap() + .to_string(); + + if provider_name.contains(',') { + return Err(format!( + "illegal ',' character in net provider name {provider_name:?}" + )); + } + + let mut hasher = std::hash::DefaultHasher::new(); + vm_name.hash(&mut hasher); + let id_hashed = hasher.finish(); + + let mac = MacAddress::new([ + 0x02, // IEEE 802c administratively assigned + 0x00, // Spectrum client + (id_hashed >> 24) as u8, + (id_hashed >> 16) as u8, + (id_hashed >> 8) as u8, + id_hashed as u8, + ]); + + Ok(NetConfig { + vhost_user: true, + vhost_socket: format!("/run/vm/by-name/{provider_name}/router-app.sock"), + id: provider_name, + mac, + }) + }) + .collect::<Result<_, _>>()?, Err(e) if e.kind() == ErrorKind::NotFound => Default::default(), Err(e) => return Err(format!("reading directory {net_providers_dir:?}: {e}")), }, diff --git a/tools/start-vmm/meson.build b/tools/start-vmm/meson.build index d07c5a0..4c79f5d 100644 --- a/tools/start-vmm/meson.build +++ b/tools/start-vmm/meson.build @@ -1,12 +1,8 @@ # SPDX-License-Identifier: EUPL-1.2+ # SPDX-FileCopyrightText: 2022-2024 Alyssa Ross <hi@alyssa.is> -c_lib = static_library('start-vmm', 'net.c', 'net-util.c', - c_args : '-D_GNU_SOURCE') - rust_lib = static_library('start_vmm', 'lib.rs', - dependencies : miniserde_dep, - link_with : c_lib) + dependencies : miniserde_dep) rust_lib_dep = declare_dependency( dependencies : miniserde_dep, @@ -20,8 +16,7 @@ executable('start-vmm', 'start-vmm.rs', if get_option('tests') test_exe = executable('start-vmm-test', 'lib.rs', dependencies : miniserde_dep, - rust_args : ['--test'], - link_with : c_lib) + rust_args : ['--test']) test('Rust unit tests', test_exe, protocol : 'rust') subdir('tests') diff --git a/tools/start-vmm/net-util.c b/tools/start-vmm/net-util.c deleted file mode 100644 index 49003e9..0000000 --- a/tools/start-vmm/net-util.c +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: EUPL-1.2+ -// SPDX-FileCopyrightText: 2022, 2024 Alyssa Ross <hi@alyssa.is> - -#include "net-util.h" - -#include <errno.h> -#include <fcntl.h> -#include <string.h> -#include <unistd.h> - -#include <sys/ioctl.h> - -#include <linux/if_tun.h> - -int tap_open(char name[static IFNAMSIZ], int flags) -{ - struct ifreq ifr; - int fd, e; - - if (strnlen(name, IFNAMSIZ) == IFNAMSIZ) { - errno = ENAMETOOLONG; - return -1; - } - - strncpy(ifr.ifr_name, name, IFNAMSIZ - 1); - ifr.ifr_flags = IFF_TAP|flags; - - if ((fd = open("/dev/net/tun", O_RDWR)) == -1) - return -1; - if (ioctl(fd, TUNSETIFF, &ifr) == -1) { - e = errno; - close(fd); - errno = e; - return -1; - } - - strncpy(name, ifr.ifr_name, IFNAMSIZ); - return fd; -} diff --git a/tools/start-vmm/net-util.h b/tools/start-vmm/net-util.h deleted file mode 100644 index 8f55206..0000000 --- a/tools/start-vmm/net-util.h +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: EUPL-1.2+ -// SPDX-FileCopyrightText: 2022 Alyssa Ross <hi@alyssa.is> - -#include <net/if.h> - -int tap_open(char name[static IFNAMSIZ], int flags); diff --git a/tools/start-vmm/net.c b/tools/start-vmm/net.c deleted file mode 100644 index 78fe7f6..0000000 --- a/tools/start-vmm/net.c +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: EUPL-1.2+ -// SPDX-FileCopyrightText: 2022-2024 Alyssa Ross <hi@alyssa.is> - -#include "ch.h" -#include "net-util.h" - -#include <assert.h> -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include <arpa/inet.h> - -#include <linux/if_tun.h> - -static int get_tap_name(char tap_name[static IFNAMSIZ], - const char tap_prefix[static 1], - const char name[static 1], int name_len) -{ - int r = snprintf(tap_name, IFNAMSIZ, "%s-%*s", tap_prefix, name_len, name); - if (r >= IFNAMSIZ) - errno = ENAMETOOLONG; - return r < 0 || r >= IFNAMSIZ ? -1 : 0; -} - -struct net_config net_setup(const char name[static 1], int name_len) -{ - int e; - unsigned int client_index; - struct net_config r = { .fd = -1, .mac = { 0 } }; - - if ((get_tap_name(r.id, "client", name, name_len)) == -1) - return r; - - if (!(client_index = htonl(if_nametoindex(r.id)))) - return r; - - if ((r.fd = tap_open(r.id, IFF_NO_PI|IFF_VNET_HDR)) == -1) - goto fail_close; - - r.mac[0] = 0x02; // IEEE 802c administratively assigned - r.mac[1] = 0x00; // Spectrum client - memcpy(&r.mac[2], &client_index, 4); - - return r; - -fail_close: - e = errno; - close(r.fd); - errno = e; - r.fd = -1; - return r; -} diff --git a/tools/start-vmm/net.rs b/tools/start-vmm/net.rs index ebfef7a..7b237df 100644 --- a/tools/start-vmm/net.rs +++ b/tools/start-vmm/net.rs @@ -2,14 +2,11 @@ // SPDX-FileCopyrightText: 2022-2025 Alyssa Ross <hi@alyssa.is> use std::borrow::Cow; -use std::ffi::{c_char, c_int}; use std::fmt::{self, Display, Formatter}; use miniserde::Serialize; use miniserde::ser::Fragment; -use crate::ch::NetConfigC; - #[repr(transparent)] #[derive(Copy, Clone)] pub struct MacAddress([u8; 6]); @@ -36,14 +33,6 @@ impl Serialize for MacAddress { } } -// SAFETY: declaration is compatible with C. -unsafe extern "C" { - /// # Safety - /// - /// The rest of the result is only valid if the returned fd is not -1. - pub fn net_setup(name: *const c_char, len: c_int) -> NetConfigC; -} - #[cfg(test)] mod tests { use super::*; diff --git a/tools/start-vmm/tests/meson.build b/tools/start-vmm/tests/meson.build index bfdfc46..5538822 100644 --- a/tools/start-vmm/tests/meson.build +++ b/tools/start-vmm/tests/meson.build @@ -4,11 +4,6 @@ rust_helper = static_library('test_helper', 'helper.rs', dependencies : rust_lib_dep) -test('tap_open', executable('tap_open', 'tap_open.c', '../net-util.c', - c_args : '-D_GNU_SOURCE')) -test('tap_open (name too long)', executable('tap_open-name-too-long', - 'tap_open-name-too-long.c', '../net-util.c', c_args : '-D_GNU_SOURCE')) - test('vm_command-basic', executable('vm_command-basic', 'vm_command-basic.rs', dependencies : rust_lib_dep, diff --git a/tools/start-vmm/tests/tap_open-name-too-long.c b/tools/start-vmm/tests/tap_open-name-too-long.c deleted file mode 100644 index ba4ebd6..0000000 --- a/tools/start-vmm/tests/tap_open-name-too-long.c +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: EUPL-1.2+ -// SPDX-FileCopyrightText: 2022 Alyssa Ross <hi@alyssa.is> - -#include "../net-util.h" - -#include <assert.h> -#include <errno.h> -#include <net/if.h> -#include <string.h> - -int main(void) -{ - char name[IFNAMSIZ]; - int fd; - - memset(name, 'a', sizeof name); - fd = tap_open(name, 0); - assert(fd == -1); - assert(errno == ENAMETOOLONG); -} diff --git a/tools/start-vmm/tests/tap_open.c b/tools/start-vmm/tests/tap_open.c deleted file mode 100644 index bf5d00c..0000000 --- a/tools/start-vmm/tests/tap_open.c +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: EUPL-1.2+ -// SPDX-FileCopyrightText: 2022 Alyssa Ross <hi@alyssa.is> - -#include "../net-util.h" - -#include <assert.h> -#include <errno.h> -#include <sched.h> -#include <string.h> - -#include <sys/ioctl.h> - -#include <linux/if_tun.h> - -int main(void) -{ - char name[IFNAMSIZ] = "tap%d"; - struct ifreq ifr; - int fd; - - unshare(CLONE_NEWUSER|CLONE_NEWNET); - - fd = tap_open(name, 0); - if (fd == -1 && (errno == EPERM || errno == ENOENT)) - return 77; - assert(!ioctl(fd, (unsigned)TUNGETIFF, &ifr)); - assert(!strcmp(name, ifr.ifr_name)); -} -- 2.51.2
This patch has been committed as 59d9caa0f72f321eb2c08b64cac76604d4422a94, which can be viewed online at https://spectrum-os.org/git/spectrum/commit/?id=59d9caa0f72f321eb2c08b64cac7.... This is an automated message. Send comments/questions/requests to: Alyssa Ross <hi@alyssa.is>
Also remove hard-coded ipv4 default route. Eventually this will be changed to have both ipv4 and ipv6 nameserver, and support IPv4 with the new router. Signed-off-by: Yureka Lilian <yureka@cyberchaos.dev> Reviewed-by: Alyssa Ross <hi@alyssa.is> --- img/app/image/etc/mdev/iface | 19 +------------------ img/app/image/etc/resolv.conf | 2 +- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/img/app/image/etc/mdev/iface b/img/app/image/etc/mdev/iface index 95f2fa8..1d81f2a 100755 --- a/img/app/image/etc/mdev/iface +++ b/img/app/image/etc/mdev/iface @@ -4,22 +4,5 @@ background { importas -Si INTERFACE - - # Our IP is encoded in the NIC-specific portion of the interface's MAC - # address. - backtick -E LOCAL_IP { - awk -PF: " - BEGIN { ex = 1 } - $1 == \"02\" && $2 == \"00\" { - printf \"100.64.%d.%d\\n\", \"0x\" $5, \"0x\" $6 - ex = 0 - } - END { exit ex } - " /sys/class/net/${INTERFACE}/address - } - - if { ip address add ${LOCAL_IP}/32 dev $INTERFACE } - if { ip link set $INTERFACE up } - if { ip route add 169.254.0.1 dev $INTERFACE } - ip route add default via 169.254.0.1 dev $INTERFACE + ip link set $INTERFACE up } diff --git a/img/app/image/etc/resolv.conf b/img/app/image/etc/resolv.conf index 7fcdf3a..e82c3d0 100644 --- a/img/app/image/etc/resolv.conf +++ b/img/app/image/etc/resolv.conf @@ -1,4 +1,4 @@ # SPDX-License-Identifier: CC0-1.0 # SPDX-FileCopyrightText: 2021 Alyssa Ross <hi@alyssa.is> -nameserver 1.1.1.1 +nameserver 2606:4700:4700::1111 -- 2.51.2
This patch has been committed as 076b5d281aa9ee54f5c9647ea922bac3f4cd70db, which can be viewed online at https://spectrum-os.org/git/spectrum/commit/?id=076b5d281aa9ee54f5c9647ea922.... This is an automated message. Send comments/questions/requests to: Alyssa Ross <hi@alyssa.is>
Signed-off-by: Yureka Lilian <yureka@cyberchaos.dev> Reviewed-by: Alyssa Ross <hi@alyssa.is> --- release/checks/integration/default.nix | 2 +- release/checks/integration/networking.c | 33 +++++++++++++++++++------ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/release/checks/integration/default.nix b/release/checks/integration/default.nix index 842b707..f8aab8a 100644 --- a/release/checks/integration/default.nix +++ b/release/checks/integration/default.nix @@ -19,7 +19,7 @@ let type = "nix"; run = writeShellScript "run" '' set -x - while :; do echo hello | ${libressl.nc}/bin/nc -Nw 2 10.0.2.2 1234; done + while :; do echo hello | ${libressl.nc}/bin/nc -Nw 2 -6 fd00::2 1234; done ''; }; diff --git a/release/checks/integration/networking.c b/release/checks/integration/networking.c index 97d7895..bc75ece 100644 --- a/release/checks/integration/networking.c +++ b/release/checks/integration/networking.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: EUPL-1.2+ // SPDX-FileCopyrightText: 2025 Alyssa Ross <hi@alyssa.is> +// SPDX-FileCopyrightText: 2025 Yureka Lilian <yureka@cyberchaos.dev> #include "lib.h" @@ -12,21 +13,27 @@ #include <net/if.h> #include <sys/ioctl.h> +#include <linux/ipv6.h> static int setup_server(void) { int fd; struct ifreq ifr; + struct in6_ifreq ifr6; - struct sockaddr_in addr = { - .sin_family = AF_INET, - .sin_port = htons(1234), - .sin_addr = { .s_addr = htonl(INADDR_LOOPBACK) }, + struct sockaddr_in6 addr = { + .sin6_family = AF_INET6, + .sin6_port = htons(1234), + .sin6_addr = { .s6_addr = { 0xfd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 } }, }; sprintf(ifr.ifr_name, "lo"); - if ((fd = socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1) { + ifr6.ifr6_ifindex = 1; + ifr6.ifr6_addr = addr.sin6_addr; + ifr6.ifr6_prefixlen = 128; + + if ((fd = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1) { perror("socket"); exit(EXIT_FAILURE); } @@ -42,11 +49,23 @@ static int setup_server(void) exit(EXIT_FAILURE); } - if (bind(fd, &addr, sizeof addr) == -1) { - perror("bind"); + if (ioctl(fd, SIOCSIFADDR, &ifr6) == -1) { + perror("SIOCSIFADDR"); + exit(EXIT_FAILURE); + } + + if ((fd = socket(AF_INET6, SOCK_STREAM|SOCK_CLOEXEC, 0)) == -1) { + perror("socket"); exit(EXIT_FAILURE); } + int tries = 0; + while (bind(fd, &addr, sizeof addr) == -1) { + perror("bind"); + if (tries++ >= 5) + exit(EXIT_FAILURE); + } + if (listen(fd, 1) == -1) { perror("listen"); exit(EXIT_FAILURE); -- 2.51.2
This patch has been committed as cdf1891ea3158147f9ffbb1695015fbb978efa26, which can be viewed online at https://spectrum-os.org/git/spectrum/commit/?id=cdf1891ea3158147f9ffbb169501.... This is an automated message. Send comments/questions/requests to: Alyssa Ross <hi@alyssa.is>
This reverts commit d8a5ef0a334667dbe87c673a80a1d525510f7e69. --- release/checks/integration/meson.build | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/release/checks/integration/meson.build b/release/checks/integration/meson.build index 8938728..7bf8f51 100644 --- a/release/checks/integration/meson.build +++ b/release/checks/integration/meson.build @@ -11,12 +11,7 @@ run_qemu = find_program('../../../scripts/run-qemu.sh') lib = static_library('spectrum-integration-test', 'lib.c') -foreach test : [ - 'appimage', - 'late-serial', - # 'networking', - 'portal', -] +foreach test : ['appimage', 'late-serial', 'networking', 'portal'] test(test, executable(test, test + '.c', link_with : lib), timeout : 400, args : [run_qemu]) -- 2.51.2
This patch has been committed as 3bb657641c93267f95ec9b7e234d22a3c6864099, which can be viewed online at https://spectrum-os.org/git/spectrum/commit/?id=3bb657641c93267f95ec9b7e234d.... This is an automated message. Send comments/questions/requests to: Alyssa Ross <hi@alyssa.is>
participants (2)
-
Alyssa Ross -
Yureka Lilian