From 583a74d8ad7545b8791e651aea7865e5f170f44b Mon Sep 17 00:00:00 2001 From: Morgan Jones Date: Sun, 9 Feb 2025 20:46:41 -0800 Subject: [PATCH] nixos/vwifi: init module This module does not currently have its own test suite: it is intended to be used in test suites for other modules that use wifi. --- nixos/modules/module-list.nix | 1 + nixos/modules/services/networking/vwifi.nix | 200 ++++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 nixos/modules/services/networking/vwifi.nix diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index f9d300e3da80..478731ecaa3d 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -1352,6 +1352,7 @@ ./services/networking/veilid.nix ./services/networking/vdirsyncer.nix ./services/networking/vsftpd.nix + ./services/networking/vwifi.nix ./services/networking/wasabibackend.nix ./services/networking/websockify.nix ./services/networking/wg-access-server.nix diff --git a/nixos/modules/services/networking/vwifi.nix b/nixos/modules/services/networking/vwifi.nix new file mode 100644 index 000000000000..64b7fbdf45a4 --- /dev/null +++ b/nixos/modules/services/networking/vwifi.nix @@ -0,0 +1,200 @@ +{ + config, + lib, + pkgs, + ... +}: + +let + inherit (lib.modules) mkIf mkMerge; + inherit (lib.options) mkOption mkPackageOption mkEnableOption; + inherit (lib.lists) optional optionals; + inherit (lib.strings) + hasSuffix + escapeShellArgs + ; + inherit (lib) types; + cfg = config.services.vwifi; +in +{ + options = { + services.vwifi = + let + mkOptionalPort = + name: + mkOption { + description = '' + The ${name} port. Set to null if we should leave it unset. + ''; + type = with types; nullOr port; + default = null; + }; + in + { + package = mkPackageOption pkgs "vwifi" { }; + module = { + enable = mkEnableOption "mac80211_hwsim module"; + numRadios = mkOption { + description = "The number of virtual radio interfaces to create."; + type = types.int; + default = 1; + }; + macPrefix = mkOption { + description = '' + The prefix for MAC addresses to use, without the trailing ':'. + If one radio is created, you can specify the whole MAC address here. + The default is defined in vwifi/src/config.h. + ''; + type = types.strMatching "^(([0-9A-Fa-f]{2}:){0,5}[0-9A-Fa-f]{2})$"; + default = "74:F8:F6"; + }; + }; + client = { + enable = mkEnableOption "vwifi client"; + spy = mkEnableOption "spy mode, useful for wireless monitors"; + serverAddress = mkOption { + description = '' + The address of the server. If set to null, will try to use the vsock protocol. + Note that this assumes that the server is spawned on the host and passed through to + QEMU, with something like: + + -device vhost-vsock-pci,id=vwifi0,guest-cid=42 + ''; + type = with types; nullOr str; + default = null; + }; + serverPort = mkOptionalPort "server port"; + extraArgs = mkOption { + description = '' + Extra arguments to pass to vwifi-client. You can use this if you want to bring + the radios up using vwifi-client instead of at boot. + ''; + type = with types; listOf str; + default = [ ]; + example = [ + "--number" + "3" + ]; + }; + }; + server = { + enable = mkEnableOption "vwifi server"; + vsock.enable = mkEnableOption "vsock kernel module"; + ports = { + vhost = mkOptionalPort "vhost"; + tcp = mkOptionalPort "TCP server"; + spy = mkOptionalPort "spy interface"; + control = mkOptionalPort "control interface"; + }; + openFirewall = mkEnableOption "opening the firewall for the TCP and spy ports"; + extraArgs = mkOption { + description = '' + Extra arguments to pass to vwifi-server. You can use this for things including + changing the ports or inducing packet loss. + ''; + type = with types; listOf str; + default = [ ]; + example = [ "--lost-packets" ]; + }; + }; + }; + }; + + config = mkMerge [ + (mkIf cfg.module.enable { + boot.kernelModules = [ + "mac80211_hwsim" + ]; + boot.extraModprobeConfig = '' + # We'll add more radios using vwifi-add-interfaces in the systemd unit. + options mac80211_hwsim radios=0 + ''; + systemd.services.vwifi-add-interfaces = mkIf (cfg.module.numRadios > 0) { + description = "vwifi interface bringup"; + wantedBy = [ "network-pre.target" ]; + serviceConfig = { + Type = "oneshot"; + ExecStart = + let + args = [ + (toString cfg.module.numRadios) + cfg.module.macPrefix + ]; + in + "${cfg.package}/bin/vwifi-add-interfaces ${escapeShellArgs args}"; + }; + }; + assertions = [ + { + assertion = !(hasSuffix ":" cfg.module.macPrefix); + message = '' + services.vwifi.module.macPrefix should not have a trailing ":". + ''; + } + ]; + }) + (mkIf cfg.client.enable { + systemd.services.vwifi-client = + let + clientArgs = + optional cfg.client.spy "--spy" + ++ optional (cfg.client.serverAddress != null) cfg.client.serverAddress + ++ optionals (cfg.client.serverPort != null) [ + "--port" + cfg.client.serverPort + ] + ++ cfg.client.extraArgs; + in + rec { + description = "vwifi client"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + requires = after; + serviceConfig = { + ExecStart = "${cfg.package}/bin/vwifi-client ${escapeShellArgs clientArgs}"; + }; + }; + }) + (mkIf cfg.server.enable { + boot.kernelModules = mkIf cfg.server.vsock.enable [ + "vhost_vsock" + ]; + networking.firewall.allowedTCPPorts = mkIf cfg.server.openFirewall ( + optional (cfg.server.ports.tcp != null) cfg.server.ports.tcp + ++ optional (cfg.server.ports.spy != null) cfg.server.ports.spy + ); + systemd.services.vwifi-server = + let + serverArgs = + optionals (cfg.server.ports.vhost != null) [ + "--port-vhost" + (toString cfg.server.ports.vhost) + ] + ++ optionals (cfg.server.ports.tcp != null) [ + "--port-tcp" + (toString cfg.server.ports.tcp) + ] + ++ optionals (cfg.server.ports.spy != null) [ + "--port-spy" + (toString cfg.server.ports.spy) + ] + ++ optionals (cfg.server.ports.control != null) [ + "--port-ctrl" + (toString cfg.server.ports.control) + ] + ++ cfg.server.extraArgs; + in + rec { + description = "vwifi server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + requires = after; + serviceConfig = { + ExecStart = "${cfg.package}/bin/vwifi-server ${escapeShellArgs serverArgs}"; + }; + }; + }) + ]; + + meta.maintainers = with lib.maintainers; [ numinit ]; +}