From c0a8792af70f61e770acd93c12f2f54869d86081 Mon Sep 17 00:00:00 2001 From: Stefan Junker Date: Tue, 26 Dec 2023 16:18:33 +0100 Subject: [PATCH] feat(router0-dmz0): AP with dynamic vlan filtering on central bridge --- flake.lock | 14 +- nix/os/devices/router0-dmz0/configuration.nix | 163 ++++++++++-------- nix/os/devices/router0-dmz0/default.nix | 5 +- nix/os/devices/router0-dmz0/flake.lock | 62 ++++--- nix/os/devices/router0-dmz0/flake.nix | 71 +++++--- 5 files changed, 189 insertions(+), 126 deletions(-) diff --git a/flake.lock b/flake.lock index b026e10..ea8adae 100644 --- a/flake.lock +++ b/flake.lock @@ -27,11 +27,11 @@ "stable": "stable" }, "locked": { - "lastModified": 1688224393, - "narHash": "sha256-rsAvFNhRFzTF7qyb6WprLFghJnRxMFjvD2e5/dqMp4I=", + "lastModified": 1699171528, + "narHash": "sha256-ZsN6y+tgN5w84oAqRQpMhIvQM39ZNSZoZvn2AK0QYr4=", "owner": "zhaofengli", "repo": "colmena", - "rev": "19384f3ee2058c56021e4465a3ec57e84a47d8dd", + "rev": "665603956a1c3040d756987bc7a810ffe86a3b15", "type": "github" }, "original": { @@ -746,16 +746,16 @@ }, "stable": { "locked": { - "lastModified": 1669735802, - "narHash": "sha256-qtG/o/i5ZWZLmXw108N2aPiVsxOcidpHJYNkT45ry9Q=", + "lastModified": 1696039360, + "narHash": "sha256-g7nIUV4uq1TOVeVIDEZLb005suTWCUjSY0zYOlSBsyE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "731cc710aeebecbf45a258e977e8b68350549522", + "rev": "32dcb45f66c0487e92db8303a798ebc548cadedc", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-22.11", + "ref": "nixos-23.05", "repo": "nixpkgs", "type": "github" } diff --git a/nix/os/devices/router0-dmz0/configuration.nix b/nix/os/devices/router0-dmz0/configuration.nix index 497ba07..f62b0e8 100644 --- a/nix/os/devices/router0-dmz0/configuration.nix +++ b/nix/os/devices/router0-dmz0/configuration.nix @@ -1,7 +1,5 @@ { - modulesPath, repoFlake, - packages', pkgs, lib, config, @@ -154,7 +152,7 @@ in { useNetworkd = true; useDHCP = false; - # No local firewall. + # these will be configured via nftables nat.enable = lib.mkForce false; firewall.enable = lib.mkForce false; @@ -278,15 +276,27 @@ in { ''; }; - # TODO: generate one of these for each vlanid - "20-br-lan.15" = { - netdevConfig = { - Kind = "vlan"; - Name = "br-lan.15"; - }; - vlanConfig.Id = 15; - }; - }; + } + # generate the vlan devices. these will be tagged on the main bridge + // builtins.foldl' + (acc: cur: acc // cur) + {} + (builtins.map + ({ vlanid, vlanid' }: { + "20-br-lan.${vlanid'}" = { + netdevConfig = { + Kind = "vlan"; + Name = "br-lan.${vlanid'}"; + }; + vlanConfig.Id = vlanid; + }; + }) + (builtins.map + (vlanid: { inherit vlanid; vlanid' = builtins.toString vlanid; }) + vlanRange + ) + ) + ; networks = { # use lan0 as secondary WAN interface "10-lan0-wan" = { @@ -377,16 +387,28 @@ in { } ]; - vlan = [ - "br-lan.15" - ]; + vlan = (builtins.map + (vlanid: "br-lan.${builtins.toString vlanid}") + vlanRange + ); }; - # TODO: generate one of these for each vlanid - "41-br-lan.15" = let - vlanid = 15; - in { - matchConfig.Name = "br-lan.15"; + } + + # configuration for the hostapd dynamic interfaces + # TODO: refactor this to configure the following per vlanid + # * netdev type vlan + # * host address for vlan + # * vlan config for wlan interface + // + builtins.foldl' + (acc: cur: acc // cur) + {} + (builtins.map ({ vlanid, vlanid' }: { + # set an address on the tagged br-lan device. + # this address will be picked up by dnsmasq. + "41-br-lan.${vlanid'}" = { + matchConfig.Name = "br-lan.${vlanid'}"; address = [ (mkVlanIpv4HostAddr { inherit vlanid; host = 1; }) ]; @@ -407,15 +429,12 @@ in { ]; }; - # TODO: generate one of these for each vlanid # configure the wlan interface as a bridge member that # * only gets traffic for vid 15 # * untags traffic after receiving it # * tags traffic that comes out of it - "41-wlan0.15" = let - vlanid = 15; - in { - matchConfig.Name = "wlan0.15"; + "41-wlan0.${vlanid'}" = { + matchConfig.Name = "wlan0.${vlanid'}"; networkConfig = { Bridge = "br-lan"; ConfigureWithoutCarrier = true; @@ -435,51 +454,38 @@ in { ]; }; - } - - # configuration for the hostapd dynamic interfaces - # TODO: refactor this to configure the following per vlanid - # * netdev type vlan - # * host address for vlan - # * vlan config for wlan interface - // - builtins.foldl' - (acc: cur: acc // cur) - {} - (builtins.map - ({ vlanid, vlanid' }: { - "50-br-lan.${vlanid'}" = { - matchConfig.Name = "br-lan.${vlanid'}"; - address = [ - (mkVlanIpv4HostAddr { inherit vlanid; host = 1; }) - ]; - networkConfig = { - ConfigureWithoutCarrier = true; - }; - linkConfig.RequiredForOnline = "no"; - - bridgeVLANs = [ - { - bridgeVLANConfig = { - # TODO debug this: each vlanid is native to each port - VLAN = vlanid; - PVID = vlanid; - }; - } - ]; + "50-br-lan.${vlanid'}" = { + matchConfig.Name = "br-lan.${vlanid'}"; + address = [ + (mkVlanIpv4HostAddr { inherit vlanid; host = 1; }) + ]; + networkConfig = { + ConfigureWithoutCarrier = true; }; - }) - (builtins.map - (vlanid: { inherit vlanid; vlanid' = builtins.toString vlanid; }) - vlanRange - ) - ); + linkConfig.RequiredForOnline = "no"; + + bridgeVLANs = [ + { + bridgeVLANConfig = { + # TODO debug this: each vlanid is native to each port + VLAN = vlanid; + PVID = vlanid; + }; + } + ]; + }; + }) + (builtins.map + (vlanid: { inherit vlanid; vlanid' = builtins.toString vlanid; }) + vlanRange + )) + ; }; # wireless access point services.hostapd = { enable = true; - package = nodeFlake.packages.hostapd_main; + package = nodeFlake.packages.${system}.hostapd_patched; radios = let # generated with https://miniwebtool.com/mac-address-generator/ mkBssid = i: "34:56:ce:0f:ed:4${toString i}"; @@ -521,19 +527,34 @@ in { # https://wireless.wiki.kernel.org/en/users/Documentation/hostapd#dynamic_vlan_tagging # https://forum.openwrt.org/t/individual-per-passphrase-wifi-vlans-using-wpa-psk-file-no-radius-required/161696/4 + dynamic_vlan = 1; + + vlan_no_bridge = 1; + + /* not used due to the above vlan_no_bridge setting vlan_tagged_interface = "br-lan"; vlan_naming = 1; vlan_bridge = "br-${iface}."; - dynamic_vlan = 1; - vlan_file = toString (pkgs.writeText "hostapd.vlan" '' + */ + + vlan_file = let + generated = builtins.map (vlanid: + "${builtins.toString vlanid} ${iface}.${builtins.toString vlanid}" + ) vlanRange + ; + + wildcard = [ # Optional wildcard entry matching all VLAN IDs. The first # in the interface # name will be replaced with the VLAN ID. The network interfaces are created # (and removed) dynamically based on the use. # see https://w1.fi/cgit/hostap/tree/hostapd/hostapd.vlan - * ${iface}.# - ''); + "* ${iface}.#" + ]; - vlan_no_bridge = 1; + file = pkgs.writeText "hostapd.vlan" + (builtins.concatStringsSep "\n" (generated ++ wildcard)); + filePath = toString file; + in filePath; wpa_key_mgmt = lib.mkForce (builtins.concatStringsSep " " [ "WPA-PSK" @@ -692,7 +713,7 @@ in { tag (mkVlanIpv4HostAddr { inherit vlanid; host = 100; cidr = false; }) (mkVlanIpv4HostAddr { inherit vlanid; host = 199; cidr = false; }) - "5m" + "30m" ]; in builtins.map @@ -748,7 +769,7 @@ in { system.stateVersion = "23.05"; - boot.kernelPackages = pkgs.linuxPackages_bpir3; + boot.kernelPackages = pkgs.linuxPackages_bpir3_latest; # We exclude a number of modules included in the default list. A non-insignificant amount do # not apply to embedded hardware like this, so simply skip the defaults. # diff --git a/nix/os/devices/router0-dmz0/default.nix b/nix/os/devices/router0-dmz0/default.nix index 59652be..e29beb2 100644 --- a/nix/os/devices/router0-dmz0/default.nix +++ b/nix/os/devices/router0-dmz0/default.nix @@ -1,11 +1,10 @@ { + system ? "aarch64-linux", nodeName, repoFlake, nodeFlake, ... -}: let - system = "aarch64-linux"; -in { +}: { meta.nodeSpecialArgs.${nodeName} = { inherit repoFlake nodeName nodeFlake system; packages' = repoFlake.packages.${system}; diff --git a/nix/os/devices/router0-dmz0/flake.lock b/nix/os/devices/router0-dmz0/flake.lock index 345a940..089ad5e 100644 --- a/nix/os/devices/router0-dmz0/flake.lock +++ b/nix/os/devices/router0-dmz0/flake.lock @@ -7,17 +7,18 @@ ] }, "locked": { - "lastModified": 1703182100, - "narHash": "sha256-zl2G9ex86b8G6J9+QT4n9g26G8dtandIt1LlFhZiaxE=", - "ref": "refs/heads/linux-6.6", - "rev": "953a04e6792c412a664212db6a64bbaaa35bff0a", - "revCount": 31, - "type": "git", - "url": "file:///home/steveej/src/steveej/nixos-bpir3" + "lastModified": 1703603768, + "narHash": "sha256-ZViXHNt7ClqNtlRO9iot+LxiSbBvZi/RR+/6Q7W6UV8=", + "owner": "steveej-forks", + "repo": "nixos-bpir3", + "rev": "47cb545b92c136d1482a66b940c4719c40eb5fe3", + "type": "github" }, "original": { - "type": "git", - "url": "file:///home/steveej/src/steveej/nixos-bpir3" + "owner": "steveej-forks", + "ref": "linux-6.6", + "repo": "nixos-bpir3", + "type": "github" } }, "dependencyDagOfSubmodule": { @@ -48,11 +49,11 @@ ] }, "locked": { - "lastModified": 1703162528, - "narHash": "sha256-pQ41wN6JlStkZOhRTIHEpuwVywLdh+xzZQW1+FzdjVs=", + "lastModified": 1703532766, + "narHash": "sha256-ojjW3cuNmqL5uqDWohwLoO8dYpheM5+AfgsNmGIMwG8=", "owner": "nix-community", "repo": "disko", - "rev": "a050895e4eb06e0738680021a701ea05dc8dbfc9", + "rev": "1b191113874dee97796749bb21eac3d84735c70a", "type": "github" }, "original": { @@ -83,11 +84,11 @@ ] }, "locked": { - "lastModified": 1703368619, - "narHash": "sha256-ZGPMYL7FMA6enhuwby961bBANmoFX14EA86m2/Jw5Jo=", + "lastModified": 1703527373, + "narHash": "sha256-AjypRssRtS6F3xkf7rE3/bXkIF2WJOZLbTIspjcE1zM=", "owner": "nix-community", "repo": "home-manager", - "rev": "a2523ea0343b056ba240abbac90ab5f116a7aa7b", + "rev": "80679ea5074ab7190c4cce478c600057cfb5edae", "type": "github" }, "original": { @@ -136,11 +137,11 @@ }, "nixos-stable": { "locked": { - "lastModified": 1702921762, - "narHash": "sha256-O/rP7gulApQAB47u6szEd8Pn8Biw0d84j5iuP2tcxzY=", + "lastModified": 1703068421, + "narHash": "sha256-WSw5Faqlw75McIflnl5v7qVD/B3S2sLh+968bpOGrWA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "d02ffbbe834b5599fc5f134e644e49397eb07188", + "rev": "d65bceaee0fb1e64363f7871bc43dc1c6ecad99f", "type": "github" }, "original": { @@ -166,6 +167,24 @@ "type": "github" } }, + "openwrt": { + "flake": false, + "locked": { + "lastModified": 1691699580, + "narHash": "sha256-CV+ufXPEr5Nz2O2FBnnuPeHNsFQ7c5s0uW39u/q3cUo=", + "ref": "main", + "rev": "847984c773d819d5579d5abae4b80a4983103ed9", + "revCount": 58166, + "type": "git", + "url": "https://github.com/openwrt/openwrt.git" + }, + "original": { + "ref": "main", + "rev": "847984c773d819d5579d5abae4b80a4983103ed9", + "type": "git", + "url": "https://github.com/openwrt/openwrt.git" + } + }, "root": { "inputs": { "bpir3": "bpir3", @@ -175,6 +194,7 @@ "hostapd": "hostapd", "nixos-nftables-firewall": "nixos-nftables-firewall", "nixpkgs": "nixpkgs", + "openwrt": "openwrt", "srvos": "srvos" } }, @@ -186,11 +206,11 @@ ] }, "locked": { - "lastModified": 1703258052, - "narHash": "sha256-gWGQxht/xRJRnA+35aHtpmev7snsM+2GBdaPyarXNqU=", + "lastModified": 1703469109, + "narHash": "sha256-hTQJ9uV43Vt8UXwervEj9mbDoQSN1mD3lwwPChG8jy8=", "owner": "numtide", "repo": "srvos", - "rev": "0c7eefd13776730f33ea28fb984dd95cb5357e8e", + "rev": "52d07db520046c4775f1047e68a05dcb53bba9ec", "type": "github" }, "original": { diff --git a/nix/os/devices/router0-dmz0/flake.nix b/nix/os/devices/router0-dmz0/flake.nix index bd9e3b4..027bdac 100644 --- a/nix/os/devices/router0-dmz0/flake.nix +++ b/nix/os/devices/router0-dmz0/flake.nix @@ -1,7 +1,6 @@ { inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - # nixpkgs.url = "github:steveej-forks/nixpkgs/hostapd-fix"; get-flake.url = "github:ursi/get-flake"; @@ -13,15 +12,29 @@ srvos.url = "github:numtide/srvos"; srvos.inputs.nixpkgs.follows = "nixpkgs"; - # bpir3.url = "github:steveej-forks/nixos-bpir3"; - bpir3.url = "/home/steveej/src/steveej/nixos-bpir3"; + bpir3.url = + "github:steveej-forks/nixos-bpir3/linux-6.6" + # "/home/steveej/src/steveej/nixos-bpir3" + ; + bpir3.inputs.nixpkgs.follows = "nixpkgs"; + nixos-nftables-firewall.url = "github:thelegy/nixos-nftables-firewall"; nixos-nftables-firewall.inputs.nixpkgs.follows = "nixpkgs"; hostapd.url = "git://w1.fi/hostap.git?branch=main"; hostapd.flake = false; + + openwrt.url = "git+https://github.com/openwrt/openwrt.git?ref=main&rev=847984c773d819d5579d5abae4b80a4983103ed9"; + openwrt.flake = false; + + # TODO: would be nice if this worked but it throws an error when using the input as a patch: + # error: flake input has unsupported input type 'file' + # hostapd_patch_vlan_no_bridge = { + # url = "file+https://raw.githubusercontent.com/openwrt/openwrt/847984c773d819d5579d5abae4b80a4983103ed9/package/network/services/hostapd/patches/710-vlan_no_bridge.patch"; + # flake = false; + # }; }; outputs = { @@ -30,25 +43,31 @@ nixpkgs, bpir3, ... - } @ attrs: let - system = "aarch64-linux"; + }: let + nativeSystem = "aarch64-linux"; nodeName = "router0-dmz0"; - pkgs = nixpkgs.legacyPackages.${system}; + + pkgs = nixpkgs.legacyPackages.${nativeSystem}; + pkgsCross = import self.inputs.nixpkgs { + system = "x86_64-linux"; + crossSystem = { + config = "aarch64-unknown-linux-gnu"; + }; + }; mkNixosConfiguration = {extraModules ? [], ...} @ attrs: nixpkgs.lib.nixosSystem ( nixpkgs.lib.attrsets.recursiveUpdate attrs { - specialArgs = { - nodeFlake = self; - repoFlake = get-flake ../../../..; + # TODO: it would be nice if this were the same as meta.nodeSpecialArgs.${nodeName} as in the default.nix + specialArgs = (import ./default.nix { + system = nativeSystem; inherit nodeName; - inherit - (bpir3.packages.${system}) - armTrustedFirmwareMT7986 - ; - }; + + repoFlake = get-flake ../../../..; + nodeFlake = self; + }).meta.nodeSpecialArgs.${nodeName}; modules = [ @@ -67,6 +86,7 @@ inherit (bpir3Pkgs) linuxPackages_bpir3 + linuxPackages_bpir3_latest ; }) @@ -79,30 +99,33 @@ in { nixosConfigurations = { native = mkNixosConfiguration { - inherit system; + system = nativeSystem; }; cross = mkNixosConfiguration { extraModules = [ { nixpkgs.buildPlatform.system = "x86_64-linux"; - nixpkgs.hostPlatform.system = system; + nixpkgs.hostPlatform.system = nativeSystem; } ]; }; }; - packages = { - hostapd_main = pkgs.hostapd.overrideDerivation(attrs: { - src = self.inputs.hostapd; - version = self.inputs.hostapd.rev; + packages = let + mkPatchedHostapd = pkgs: pkgs.hostapd.overrideDerivation(attrs: { patches = attrs.patches ++ [ - (builtins.fetchurl { - url = "https://raw.githubusercontent.com/openwrt/openwrt/main/package/network/services/hostapd/patches/710-vlan_no_bridge.patch"; - sha256 = "sha256:1p6fjjdwx5xrxyvllfrmvdkiji7bgx997k1qmp199bbic1fq6ks9"; - }) + "${self.inputs.openwrt}/package/network/services/hostapd/patches/710-vlan_no_bridge.patch" ]; }); + in { + "${nativeSystem}" = { + hostapd_patched = mkPatchedHostapd pkgs; + }; + + cross = { + hostapd_patched = mkPatchedHostapd pkgsCross; + }; }; }; }