From 5341235bc3435efa4cf3015954e06ca278570c1f Mon Sep 17 00:00:00 2001 From: Pim Kunis Date: Mon, 28 Oct 2024 14:12:06 +0100 Subject: [PATCH] Format repo --- deploy.nix | 34 +- flake.lock | 116 ++- flake.nix | 7 +- formatter.nix | 19 +- machines/default.nix | 40 +- machines/pikvm.nix | 9 +- machines/talos.nix | 2 +- modules/backups.nix | 27 +- modules/data-sharing.nix | 18 +- modules/default.nix | 48 +- modules/k3s/bootstrap.nix | 4 +- modules/k3s/default.nix | 148 +-- modules/monitoring/default.nix | 37 +- modules/networking/default.nix | 6 +- modules/storage.nix | 16 +- modules/tailscale.nix | 26 +- nixos.nix | 21 +- scripts/default.nix | 34 +- shell.nix | 13 +- utils/default.nix | 24 +- utils/net.nix | 1627 +++++++++++++++----------------- 21 files changed, 1200 insertions(+), 1076 deletions(-) diff --git a/deploy.nix b/deploy.nix index 5da2ab6..e8886c7 100644 --- a/deploy.nix +++ b/deploy.nix @@ -1,26 +1,26 @@ -{ self, deploy-rs, ... }: -let +{ + self, + deploy-rs, + ... +}: let deployArch = "x86_64-linux"; mkDeployNodes = nodeDef: builtins.mapAttrs - (name: machine: nodeDef name machine) - self.machines.${deployArch}; -in -{ + (name: machine: nodeDef name machine) + self.machines.${deployArch}; +in { deploy = { sshUser = "root"; user = "root"; - nodes = mkDeployNodes (name: machine: - let - nixosConfiguration = self.nixosConfigurations.${name}; - in - { - hostname = nixosConfiguration.config.networking.fqdn; - profiles.system = { - remoteBuild = machine.arch != deployArch; - path = deploy-rs.lib.${machine.arch}.activate.nixos nixosConfiguration; - }; - }); + nodes = mkDeployNodes (name: machine: let + nixosConfiguration = self.nixosConfigurations.${name}; + in { + hostname = nixosConfiguration.config.networking.fqdn; + profiles.system = { + remoteBuild = machine.arch != deployArch; + path = deploy-rs.lib.${machine.arch}.activate.nixos nixosConfiguration; + }; + }); }; } diff --git a/flake.lock b/flake.lock index 34d0281..074e833 100644 --- a/flake.lock +++ b/flake.lock @@ -109,6 +109,22 @@ "type": "github" } }, + "flake-compat_4": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": [ @@ -163,6 +179,27 @@ "type": "github" } }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "kubenix": { "inputs": { "flake-compat": "flake-compat_2", @@ -262,6 +299,22 @@ } }, "nixpkgs-stable": { + "locked": { + "lastModified": 1720386169, + "narHash": "sha256-NGKVY4PjzwAa4upkGtAMz1npHGoRzWotlSnVlqI40mo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "194846768975b7ad2c4988bdb82572c00222c0d7", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-stable_2": { "locked": { "lastModified": 1729357638, "narHash": "sha256-66RHecx+zohbZwJVEPF7uuwHeqf8rykZTMCTqIrOew4=", @@ -309,6 +362,45 @@ "type": "github" } }, + "nixpkgs_3": { + "locked": { + "lastModified": 1726871744, + "narHash": "sha256-V5LpfdHyQkUF7RfOaDPrZDP+oqz88lTJrMT1+stXNwo=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "a1d92660c6b3b7c26fb883500a80ea9d33321be2", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat_4", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs-unstable" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1729104314, + "narHash": "sha256-pZRZsq5oCdJt3upZIU4aslS9XwFJ+/nVtALHIciX/BI=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "3c3e88f0f544d6bb54329832616af7eb971b6be6", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { "deploy-rs": "deploy-rs", @@ -321,7 +413,9 @@ "nixos-hardware": "nixos-hardware", "nixpkgs": "nixpkgs_2", "nixpkgs-unstable": "nixpkgs-unstable", - "sops-nix": "sops-nix" + "pre-commit-hooks": "pre-commit-hooks", + "sops-nix": "sops-nix", + "treefmt-nix": "treefmt-nix" } }, "sops-nix": { @@ -329,7 +423,7 @@ "nixpkgs": [ "nixpkgs" ], - "nixpkgs-stable": "nixpkgs-stable" + "nixpkgs-stable": "nixpkgs-stable_2" }, "locked": { "lastModified": 1729775275, @@ -410,6 +504,24 @@ "type": "github" } }, + "treefmt-nix": { + "inputs": { + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1730025913, + "narHash": "sha256-Y9NtFmP8ciLyRsopcCx1tyoaaStKeq+EndwtGCgww7I=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "bae131e525cc8718da22fbeb8d8c7c43c4ea502a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + }, "utils": { "inputs": { "systems": "systems" diff --git a/flake.nix b/flake.nix index 3b34261..0238d94 100644 --- a/flake.nix +++ b/flake.nix @@ -55,8 +55,11 @@ }; }; - outputs = - inputs@{ nixpkgs, flake-utils, ... }: + outputs = inputs @ { + nixpkgs, + flake-utils, + ... + }: flake-utils.lib.meld inputs [ ./scripts ./deploy.nix diff --git a/formatter.nix b/formatter.nix index 8696b53..0d42f9c 100644 --- a/formatter.nix +++ b/formatter.nix @@ -1,9 +1,14 @@ -{ nixpkgs, treefmt-nix, flake-utils, ...}: flake-utils.lib.eachDefaultSystem (system: -let - pkgs = nixpkgs.legacyPackages.${system}; - treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix; -in { - formatter = treefmtEval.config.build.wrapper; -} + nixpkgs, + treefmt-nix, + flake-utils, + ... +}: +flake-utils.lib.eachDefaultSystem ( + system: let + pkgs = nixpkgs.legacyPackages.${system}; + treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix; + in { + formatter = treefmtEval.config.build.wrapper; + } ) diff --git a/machines/default.nix b/machines/default.nix index 74aa74e..261980b 100644 --- a/machines/default.nix +++ b/machines/default.nix @@ -1,9 +1,13 @@ -{ nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system: -let +{ + nixpkgs, + flake-utils, + ... +}: +flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; lib = pkgs.lib; - machineOpts = { config, ... }: { + machineOpts = {config, ...}: { options = { arch = lib.mkOption { default = null; @@ -19,7 +23,7 @@ let }; nixosModule = lib.mkOption { - default = { ... }: { }; + default = {...}: {}; type = lib.types.anything; description = '' Customized configuration for this machine in the form of a NixOS module. @@ -43,17 +47,19 @@ let }; }; }; -in -{ - machines = (lib.modules.evalModules { - modules = [ - allOpts - ./warwick.nix - ./atlas.nix - ./jefke.nix - ./lewis.nix - # ./talos.nix - # ./pikvm.nix - ]; - }).config.machines; +in { + machines = + (lib.modules.evalModules { + modules = [ + allOpts + ./warwick.nix + ./atlas.nix + ./jefke.nix + ./lewis.nix + # ./talos.nix + # ./pikvm.nix + ]; + }) + .config + .machines; }) diff --git a/machines/pikvm.nix b/machines/pikvm.nix index 6a7bc14..d384897 100644 --- a/machines/pikvm.nix +++ b/machines/pikvm.nix @@ -3,7 +3,12 @@ arch = "aarch64-linux"; isRaspberryPi = true; - nixosModule = { config, inputs, lib, ... }: { + nixosModule = { + config, + inputs, + lib, + ... + }: { # imports = [ "${inputs.nixpkgs}/nixos/modules/installer/sd-card/sd-image-aarch64.nix" ]; lab = { storage.profile = "pi"; @@ -17,7 +22,7 @@ v4l-utils ]; - boot.extraModulePackages = with config.boot.kernelPackages; [ v4l2loopback ]; + boot.extraModulePackages = with config.boot.kernelPackages; [v4l2loopback]; }; }; } diff --git a/machines/talos.nix b/machines/talos.nix index 0fa0311..56ada95 100644 --- a/machines/talos.nix +++ b/machines/talos.nix @@ -2,7 +2,7 @@ machines.talos = { arch = "x86_64-linux"; - nixosModule = { lib, ... }: { + nixosModule = {lib, ...}: { lab.storage.profile = "normal"; # boot.loader.systemd-boot.enable = lib.mkForce false; diff --git a/modules/backups.nix b/modules/backups.nix index bf1c9a5..18e7240 100644 --- a/modules/backups.nix +++ b/modules/backups.nix @@ -1,12 +1,16 @@ -{ pkgs, lib, config, ... }: -let +{ + pkgs, + lib, + config, + ... +}: let cfg = config.lab.backups; borgmaticConfig = pkgs.writeTextFile { name = "borgmatic-config.yaml"; - text = lib.generators.toYAML { } { - source_directories = [ "/mnt/longhorn/persistent/longhorn-backup" ]; + text = lib.generators.toYAML {} { + source_directories = ["/mnt/longhorn/persistent/longhorn-backup"]; repositories = [ { @@ -27,8 +31,7 @@ let encryption_passcommand = "${pkgs.coreutils}/bin/cat ${config.sops.secrets."borg/borgPassphrase".path}"; }; }; -in -{ +in { options.lab.backups = { enable = lib.mkOption { default = false; @@ -48,13 +51,13 @@ in }; config = lib.mkIf cfg.enable { - environment.systemPackages = with pkgs; [ borgbackup ]; + environment.systemPackages = with pkgs; [borgbackup]; # Converted from: # https://github.com/borgmatic-collective/borgmatic/tree/84823dfb912db650936e3492f6ead7e0e0d32a0f/sample/systemd systemd.services.borgmatic = { description = "borgmatic backup"; - wants = [ "network-online.target" ]; - after = [ "network-online.target" ]; + wants = ["network-online.target"]; + after = ["network-online.target"]; unitConfig.ConditionACPower = true; preStart = "${pkgs.coreutils}/bin/sleep 10s"; @@ -75,7 +78,7 @@ in systemd.timers.borgmatic = { description = "Run borgmatic backup"; - wantedBy = [ "timers.target" ]; + wantedBy = ["timers.target"]; timerConfig = { OnCalendar = "*-*-* 3:00:00"; Persistent = true; @@ -84,8 +87,8 @@ in }; sops.secrets = { - "borg/borgPassphrase" = { }; - "borg/borgbasePrivateKey" = { }; + "borg/borgPassphrase" = {}; + "borg/borgbasePrivateKey" = {}; }; }; } diff --git a/modules/data-sharing.nix b/modules/data-sharing.nix index ac6c456..8fe6c37 100644 --- a/modules/data-sharing.nix +++ b/modules/data-sharing.nix @@ -1,5 +1,8 @@ -{ lib, config, ... }: -let +{ + lib, + config, + ... +}: let cfg = config.lab.data-sharing; nfsShares = [ @@ -13,13 +16,12 @@ let nfsExports = lib.strings.concatLines ( builtins.map - (share: - "${share} 192.168.30.0/16(rw,sync,no_subtree_check,no_root_squash) 127.0.0.1/8(rw,sync,no_subtree_check,no_root_squash) 10.0.0.0/8(rw,sync,no_subtree_check,no_root_squash)" - ) - nfsShares + ( + share: "${share} 192.168.30.0/16(rw,sync,no_subtree_check,no_root_squash) 127.0.0.1/8(rw,sync,no_subtree_check,no_root_squash) 10.0.0.0/8(rw,sync,no_subtree_check,no_root_squash)" + ) + nfsShares ); -in -{ +in { options.lab.data-sharing = { enable = lib.mkOption { default = false; diff --git a/modules/default.nix b/modules/default.nix index 7628bdb..8749267 100644 --- a/modules/default.nix +++ b/modules/default.nix @@ -1,17 +1,27 @@ -{ self, pkgs, config, lib, inputs, machine, ... }: { - imports = [ - ./storage.nix - ./backups.nix - ./networking - ./data-sharing.nix - ./monitoring - ./k3s - ./tailscale.nix - machine.nixosModule - inputs.disko.nixosModules.disko - inputs.sops-nix.nixosModules.sops - inputs.nix-snapshotter.nixosModules.nix-snapshotter - ] ++ lib.lists.optional (machine.isRaspberryPi) inputs.nixos-hardware.nixosModules.raspberry-pi-4; +{ + self, + pkgs, + config, + lib, + inputs, + machine, + ... +}: { + imports = + [ + ./storage.nix + ./backups.nix + ./networking + ./data-sharing.nix + ./monitoring + ./k3s + ./tailscale.nix + machine.nixosModule + inputs.disko.nixosModules.disko + inputs.sops-nix.nixosModules.sops + inputs.nix-snapshotter.nixosModules.nix-snapshotter + ] + ++ lib.lists.optional (machine.isRaspberryPi) inputs.nixos-hardware.nixosModules.raspberry-pi-4; config = { time.timeZone = "Europe/Amsterdam"; @@ -31,7 +41,9 @@ i18n = { defaultLocale = "en_US.UTF-8"; - extraLocaleSettings = let extraLocale = "nl_NL.UTF-8"; in { + extraLocaleSettings = let + extraLocale = "nl_NL.UTF-8"; + in { LC_ADDRESS = extraLocale; LC_IDENTIFICATION = extraLocale; LC_MEASUREMENT = extraLocale; @@ -97,12 +109,12 @@ ]; boot = lib.mkIf (! machine.isRaspberryPi) { - kernelModules = [ "kvm-intel" ]; - extraModulePackages = [ ]; + kernelModules = ["kvm-intel"]; + extraModulePackages = []; kernel.sysctl."fs.inotify.max_user_instances" = 256; initrd = { - kernelModules = [ ]; + kernelModules = []; availableKernelModules = [ "ahci" diff --git a/modules/k3s/bootstrap.nix b/modules/k3s/bootstrap.nix index 28a8aa9..e49770b 100644 --- a/modules/k3s/bootstrap.nix +++ b/modules/k3s/bootstrap.nix @@ -1,5 +1,5 @@ -{ kubenix, ... }: { - imports = [ kubenix.modules.k8s ]; +{kubenix, ...}: { + imports = [kubenix.modules.k8s]; kubernetes.resources.clusterRoleBindings.cluster-admins = { roleRef = { apiGroup = "rbac.authorization.k8s.io"; diff --git a/modules/k3s/default.nix b/modules/k3s/default.nix index d638c81..9b00024 100644 --- a/modules/k3s/default.nix +++ b/modules/k3s/default.nix @@ -1,8 +1,13 @@ -{ self, inputs, pkgs, lib, config, ... }: -let - cfg = config.lab.k3s; -in { + self, + inputs, + pkgs, + lib, + config, + ... +}: let + cfg = config.lab.k3s; +in { options.lab.k3s = { enable = lib.mkOption { default = false; @@ -62,68 +67,66 @@ in address = "/run/nix-snapshotter/nix-snapshotter.sock"; }; - plugins = - let - k3s-cni-plugins = pkgs.buildEnv { - name = "k3s-cni-plugins"; - paths = with pkgs; [ - cni-plugins - cni-plugin-flannel - ]; - }; - in - { - "io.containerd.grpc.v1.cri" = { - stream_server_address = "127.0.0.1"; - stream_server_port = "10010"; - enable_selinux = false; - enable_unprivileged_ports = true; - enable_unprivileged_icmp = true; - disable_apparmor = true; - disable_cgroup = true; - restrict_oom_score_adj = true; - sandbox_image = "rancher/mirrored-pause:3.6"; - containerd.snapshotter = "nix"; + plugins = let + k3s-cni-plugins = pkgs.buildEnv { + name = "k3s-cni-plugins"; + paths = with pkgs; [ + cni-plugins + cni-plugin-flannel + ]; + }; + in { + "io.containerd.grpc.v1.cri" = { + stream_server_address = "127.0.0.1"; + stream_server_port = "10010"; + enable_selinux = false; + enable_unprivileged_ports = true; + enable_unprivileged_icmp = true; + disable_apparmor = true; + disable_cgroup = true; + restrict_oom_score_adj = true; + sandbox_image = "rancher/mirrored-pause:3.6"; + containerd.snapshotter = "nix"; - cni = { - conf_dir = "/var/lib/rancher/k3s/agent/etc/cni/net.d/"; - bin_dir = "${k3s-cni-plugins}/bin"; - }; + cni = { + conf_dir = "/var/lib/rancher/k3s/agent/etc/cni/net.d/"; + bin_dir = "${k3s-cni-plugins}/bin"; }; + }; - "io.containerd.transfer.v1.local".unpack_config = [{ + "io.containerd.transfer.v1.local".unpack_config = [ + { platform = "linux/amd64"; snapshotter = "nix"; - }]; - }; + } + ]; + }; }; }; services = { nix-snapshotter.enable = true; - k3s = - let - serverFlagList = [ - "--image-service-endpoint=unix:///run/nix-snapshotter/nix-snapshotter.sock" - "--snapshotter=overlayfs" - "--container-runtime-endpoint=unix:///run/containerd/containerd.sock" - "--tls-san=${config.networking.fqdn}" - "--disable=servicelb" - "--cluster-cidr=10.42.0.0/16,2001:cafe:42::/56" - "--service-cidr=10.43.0.0/16,2001:cafe:43::/112" - ]; + k3s = let + serverFlagList = [ + "--image-service-endpoint=unix:///run/nix-snapshotter/nix-snapshotter.sock" + "--snapshotter=overlayfs" + "--container-runtime-endpoint=unix:///run/containerd/containerd.sock" + "--tls-san=${config.networking.fqdn}" + "--disable=servicelb" + "--cluster-cidr=10.42.0.0/16,2001:cafe:42::/56" + "--service-cidr=10.43.0.0/16,2001:cafe:43::/112" + ]; - serverFlags = builtins.concatStringsSep " " serverFlagList; - in - { - enable = true; - role = cfg.role; - tokenFile = config.sops.secrets."k3s/serverToken".path; - extraFlags = lib.mkIf (cfg.role == "server") (lib.mkForce serverFlags); - clusterInit = cfg.clusterInit; - serverAddr = lib.mkIf (! (cfg.serverAddr == null)) cfg.serverAddr; - }; + serverFlags = builtins.concatStringsSep " " serverFlagList; + in { + enable = true; + role = cfg.role; + tokenFile = config.sops.secrets."k3s/serverToken".path; + extraFlags = lib.mkIf (cfg.role == "server") (lib.mkForce serverFlags); + clusterInit = cfg.clusterInit; + serverAddr = lib.mkIf (! (cfg.serverAddr == null)) cfg.serverAddr; + }; # Required for Longhorn openiscsi = { @@ -143,11 +146,14 @@ in k3s-bootstrap = lib.mkIf (cfg.role == "server") { text = ( let - k3sBootstrapFile = (inputs.kubenix.evalModules.x86_64-linux { - module = import ./bootstrap.nix; - }).config.kubernetes.result; - in - '' + k3sBootstrapFile = + (inputs.kubenix.evalModules.x86_64-linux { + module = import ./bootstrap.nix; + }) + .config + .kubernetes + .result; + in '' mkdir -p /var/lib/rancher/k3s/server/manifests ln -sf ${k3sBootstrapFile} /var/lib/rancher/k3s/server/manifests/k3s-bootstrap.json '' @@ -166,18 +172,16 @@ in }; }; - sops.secrets = - let - keyPathBase = "/var/lib/rancher/k3s/server/tls"; - in - { - "k3s/serverToken" = { }; - "k3s/keys/clientCAKey".path = "${keyPathBase}/client-ca.key"; - "k3s/keys/requestHeaderCAKey".path = "${keyPathBase}/request-header-ca.key"; - "k3s/keys/serverCAKey".path = "${keyPathBase}/server-ca.key"; - "k3s/keys/serviceKey".path = "${keyPathBase}/service.key"; - "k3s/keys/etcd/peerCAKey".path = "${keyPathBase}/etcd/peer-ca.key"; - "k3s/keys/etcd/serverCAKey".path = "${keyPathBase}/etcd/server-ca.key"; - }; + sops.secrets = let + keyPathBase = "/var/lib/rancher/k3s/server/tls"; + in { + "k3s/serverToken" = {}; + "k3s/keys/clientCAKey".path = "${keyPathBase}/client-ca.key"; + "k3s/keys/requestHeaderCAKey".path = "${keyPathBase}/request-header-ca.key"; + "k3s/keys/serverCAKey".path = "${keyPathBase}/server-ca.key"; + "k3s/keys/serviceKey".path = "${keyPathBase}/service.key"; + "k3s/keys/etcd/peerCAKey".path = "${keyPathBase}/etcd/peer-ca.key"; + "k3s/keys/etcd/serverCAKey".path = "${keyPathBase}/etcd/server-ca.key"; + }; }; } diff --git a/modules/monitoring/default.nix b/modules/monitoring/default.nix index 2d6560d..a67b12e 100644 --- a/modules/monitoring/default.nix +++ b/modules/monitoring/default.nix @@ -1,8 +1,11 @@ -{ lib, config, machines, ... }: -let - cfg = config.lab.monitoring; -in { + lib, + config, + machines, + ... +}: let + cfg = config.lab.monitoring; +in { options = { lab.monitoring = { enable = lib.mkOption { @@ -18,8 +21,9 @@ in }; config = lib.mkIf cfg.enable { - networking.firewall.allowedTCPPorts = [ config.services.prometheus.exporters.node.port ] - ++ lib.lists.optionals cfg.server.enable [ 80 ]; + networking.firewall.allowedTCPPorts = + [config.services.prometheus.exporters.node.port] + ++ lib.lists.optionals cfg.server.enable [80]; services.prometheus = { enable = cfg.server.enable; @@ -32,12 +36,15 @@ in scrapeConfigs = lib.mkIf cfg.server.enable ( let - generated = lib.attrsets.mapAttrsToList + generated = + lib.attrsets.mapAttrsToList (name: machine: { job_name = name; - static_configs = [{ - targets = [ "${name}.dmz:${toString config.services.prometheus.exporters.node.port}" ]; - }]; + static_configs = [ + { + targets = ["${name}.dmz:${toString config.services.prometheus.exporters.node.port}"]; + } + ]; }) machines; @@ -53,12 +60,14 @@ in password = "admin"; }; - static_configs = [{ - targets = [ "pikvm.dmz" ]; - }]; + static_configs = [ + { + targets = ["pikvm.dmz"]; + } + ]; }; in - generated ++ [ pikvm ] + generated ++ [pikvm] ); }; diff --git a/modules/networking/default.nix b/modules/networking/default.nix index df36440..0f235cc 100644 --- a/modules/networking/default.nix +++ b/modules/networking/default.nix @@ -1,4 +1,8 @@ -{ lib, machine, ... }: { +{ + lib, + machine, + ... +}: { config = { networking = { domain = "dmz"; diff --git a/modules/storage.nix b/modules/storage.nix index 670cb7f..d9e5529 100644 --- a/modules/storage.nix +++ b/modules/storage.nix @@ -1,5 +1,8 @@ -{ lib, config, ... }: -let +{ + lib, + config, + ... +}: let cfg = config.lab.storage; modules = [ { @@ -7,7 +10,7 @@ let fileSystems."/" = { device = "/dev/disk/by-label/NIXOS_SD"; fsType = "ext4"; - options = [ "noatime" ]; + options = ["noatime"]; }; }; } @@ -87,7 +90,7 @@ let type = "filesystem"; format = "ext4"; mountpoint = "/"; - mountOptions = [ "defaults" ]; + mountOptions = ["defaults"]; }; }; @@ -145,7 +148,7 @@ let type = "filesystem"; format = "ext4"; mountpoint = "/"; - mountOptions = [ "defaults" ]; + mountOptions = ["defaults"]; }; }; }; @@ -155,8 +158,7 @@ let }; } ]; -in -{ +in { imports = modules; options.lab.storage = { diff --git a/modules/tailscale.nix b/modules/tailscale.nix index d50408e..d24c4d7 100644 --- a/modules/tailscale.nix +++ b/modules/tailscale.nix @@ -1,8 +1,10 @@ -{ lib, config, ... }: -let - cfg = config.lab.tailscale; -in { + lib, + config, + ... +}: let + cfg = config.lab.tailscale; +in { options = { lab.tailscale = { enable = lib.mkEnableOption "tailscale"; @@ -21,15 +23,17 @@ in useRoutingFeatures = "server"; openFirewall = true; - extraUpFlags = [ - "--accept-dns=false" - "--hostname=${config.networking.hostName}" - ] ++ lib.lists.optional cfg.advertiseExitNode "--advertise-exit-node" - ++ lib.lists.optional cfg.advertiseExitNode "--advertise-routes=192.168.30.0/24"; + extraUpFlags = + [ + "--accept-dns=false" + "--hostname=${config.networking.hostName}" + ] + ++ lib.lists.optional cfg.advertiseExitNode "--advertise-exit-node" + ++ lib.lists.optional cfg.advertiseExitNode "--advertise-routes=192.168.30.0/24"; }; - sops.secrets."tailscale/authKey" = { }; + sops.secrets."tailscale/authKey" = {}; - systemd.network.wait-online.ignoredInterfaces = [ "tailscale0" ]; + systemd.network.wait-online.ignoredInterfaces = ["tailscale0"]; }; } diff --git a/nixos.nix b/nixos.nix index 21c0023..4d2d0ad 100644 --- a/nixos.nix +++ b/nixos.nix @@ -1,23 +1,26 @@ -{ self, nixpkgs, ... }@inputs: -let +{ + self, + nixpkgs, + ... +} @ inputs: let deployArch = "x86_64-linux"; machines = self.machines.${deployArch}; mkNixosSystems = systemDef: builtins.mapAttrs - (name: machine: + ( + name: machine: nixpkgs.lib.nixosSystem (systemDef name machine) - ) - machines; -in -{ + ) + machines; +in { nixosConfigurations = mkNixosSystems (name: machine: { system = machine.arch; - specialArgs = { inherit self inputs machine machines; }; + specialArgs = {inherit self inputs machine machines;}; modules = [ "${self}/modules" - { networking.hostName = name; } + {networking.hostName = name;} ]; }); } diff --git a/scripts/default.nix b/scripts/default.nix index e1ffbbc..5386881 100644 --- a/scripts/default.nix +++ b/scripts/default.nix @@ -1,23 +1,31 @@ -{ nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system: -let +{ + nixpkgs, + flake-utils, + ... +}: +flake-utils.lib.eachDefaultSystem (system: let pkgs = nixpkgs.legacyPackages.${system}; - createScript = { name, runtimeInputs, scriptPath, extraWrapperFlags ? "", ... }: - let - script = (pkgs.writeScriptBin name (builtins.readFile scriptPath)).overrideAttrs (old: { - buildCommand = "${old.buildCommand}\n patchShebangs $out"; - }); - in + createScript = { + name, + runtimeInputs, + scriptPath, + extraWrapperFlags ? "", + ... + }: let + script = (pkgs.writeScriptBin name (builtins.readFile scriptPath)).overrideAttrs (old: { + buildCommand = "${old.buildCommand}\n patchShebangs $out"; + }); + in pkgs.symlinkJoin { inherit name; - paths = [ script ] ++ runtimeInputs; - buildInputs = [ pkgs.makeWrapper ]; + paths = [script] ++ runtimeInputs; + buildInputs = [pkgs.makeWrapper]; postBuild = "wrapProgram $out/bin/${name} --set PATH $out/bin ${extraWrapperFlags}"; }; -in -{ +in { packages.bootstrap = createScript { name = "bootstrap"; - runtimeInputs = with pkgs; [ sops coreutils nixos-anywhere ]; + runtimeInputs = with pkgs; [sops coreutils nixos-anywhere]; scriptPath = ./bootstrap.sh; }; }) diff --git a/shell.nix b/shell.nix index 67e7b03..072adef 100644 --- a/shell.nix +++ b/shell.nix @@ -1,9 +1,12 @@ -{ flake-utils, nixpkgs, ... }: flake-utils.lib.eachDefaultSystem (system: -let - pkgs = nixpkgs.legacyPackages.${system}; -in { + flake-utils, + nixpkgs, + ... +}: +flake-utils.lib.eachDefaultSystem (system: let + pkgs = nixpkgs.legacyPackages.${system}; +in { devShells.default = pkgs.mkShell { - buildInputs = with pkgs; [ ansible ]; + buildInputs = with pkgs; [ansible]; }; }) diff --git a/utils/default.nix b/utils/default.nix index 297fc3c..e73e360 100644 --- a/utils/default.nix +++ b/utils/default.nix @@ -1,14 +1,14 @@ -{ nixpkgs, flake-utils, ... }: - -let - systemAttrs = flake-utils.lib.eachDefaultSystem (system: - let - pkgs = nixpkgs.legacypackages.${system}; - lib = pkgs.lib; - in - { - net = import ./net.nix lib; - }); +{ + nixpkgs, + flake-utils, + ... +}: let + systemAttrs = flake-utils.lib.eachDefaultSystem (system: let + pkgs = nixpkgs.legacypackages.${system}; + lib = pkgs.lib; + in { + net = import ./net.nix lib; + }); nonSystemAttrs = { globals = import ./globals.nix; @@ -16,4 +16,4 @@ let allAttrs = systemAttrs // nonSystemAttrs; in -allAttrs + allAttrs diff --git a/utils/net.nix b/utils/net.nix index 9f5b0e5..d5b15a1 100644 --- a/utils/net.nix +++ b/utils/net.nix @@ -1,367 +1,347 @@ # IP address arithmetic and validation in Nix by @duairc: # https://gist.github.com/duairc/5c9bb3c922e5d501a1edb9e7b3b845ba - -lib: - -let - - net = { - ip = { - - # add :: (ip | mac | integer) -> ip -> ip - # - # Examples: - # - # Adding integer to IPv4: - # > net.ip.add 100 "10.0.0.1" - # "10.0.0.101" - # - # Adding IPv4 to IPv4: - # > net.ip.add "127.0.0.1" "10.0.0.1" - # "137.0.0.2" - # - # Adding IPv6 to IPv4: - # > net.ip.add "::cafe:beef" "10.0.0.1" - # "212.254.186.191" - # - # Adding MAC to IPv4 (overflows): - # > net.ip.add "fe:ed:fa:ce:f0:0d" "10.0.0.1" - # "4.206.240.14" - # - # Adding integer to IPv6: - # > net.ip.add 100 "dead:cafe:beef::" - # "dead:cafe:beef::64" - # - # Adding IPv4 to to IPv6: - # > net.ip.add "127.0.0.1" "dead:cafe:beef::" - # "dead:cafe:beef::7f00:1" - # - # Adding MAC to IPv6: - # > net.ip.add "fe:ed:fa:ce:f0:0d" "dead:cafe:beef::" - # "dead:cafe:beef::feed:face:f00d" - add = delta: ip: - let +lib: let + net = + { + ip = { + # add :: (ip | mac | integer) -> ip -> ip + # + # Examples: + # + # Adding integer to IPv4: + # > net.ip.add 100 "10.0.0.1" + # "10.0.0.101" + # + # Adding IPv4 to IPv4: + # > net.ip.add "127.0.0.1" "10.0.0.1" + # "137.0.0.2" + # + # Adding IPv6 to IPv4: + # > net.ip.add "::cafe:beef" "10.0.0.1" + # "212.254.186.191" + # + # Adding MAC to IPv4 (overflows): + # > net.ip.add "fe:ed:fa:ce:f0:0d" "10.0.0.1" + # "4.206.240.14" + # + # Adding integer to IPv6: + # > net.ip.add 100 "dead:cafe:beef::" + # "dead:cafe:beef::64" + # + # Adding IPv4 to to IPv6: + # > net.ip.add "127.0.0.1" "dead:cafe:beef::" + # "dead:cafe:beef::7f00:1" + # + # Adding MAC to IPv6: + # > net.ip.add "fe:ed:fa:ce:f0:0d" "dead:cafe:beef::" + # "dead:cafe:beef::feed:face:f00d" + add = delta: ip: let function = "net.ip.add"; delta' = typechecks.numeric function "delta" delta; ip' = typechecks.ip function "ip" ip; in - builders.ip (implementations.ip.add delta' ip'); + builders.ip (implementations.ip.add delta' ip'); - # diff :: ip -> ip -> (integer | ipv6) - # - # net.ip.diff is the reverse of net.ip.add: - # - # net.ip.diff (net.ip.add a b) a = b - # net.ip.diff (net.ip.add a b) b = a - # - # The difference between net.ip.diff and net.ip.subtract is that - # net.ip.diff will try its best to return an integer (falling back - # to an IPv6 if the result is too big to fit in an integer). This is - # useful if you have two hosts that you know are on the same network - # and you just want to calculate the offset between them — a result - # like "0.0.0.10" is not very useful (which is what you would get - # from net.ip.subtract). - diff = minuend: subtrahend: - let + # diff :: ip -> ip -> (integer | ipv6) + # + # net.ip.diff is the reverse of net.ip.add: + # + # net.ip.diff (net.ip.add a b) a = b + # net.ip.diff (net.ip.add a b) b = a + # + # The difference between net.ip.diff and net.ip.subtract is that + # net.ip.diff will try its best to return an integer (falling back + # to an IPv6 if the result is too big to fit in an integer). This is + # useful if you have two hosts that you know are on the same network + # and you just want to calculate the offset between them — a result + # like "0.0.0.10" is not very useful (which is what you would get + # from net.ip.subtract). + diff = minuend: subtrahend: let function = "net.ip.diff"; minuend' = typechecks.ip function "minuend" minuend; subtrahend' = typechecks.ip function "subtrahend" subtrahend; result = implementations.ip.diff minuend' subtrahend'; in - if result ? ipv6 - then builders.ipv6 result - else result; + if result ? ipv6 + then builders.ipv6 result + else result; - # subtract :: (ip | mac | integer) -> ip -> ip - # - # net.ip.subtract is also the reverse of net.ip.add: - # - # net.ip.subtract a (net.ip.add a b) = b - # net.ip.subtract b (net.ip.add a b) = a - # - # The difference between net.ip.subtract and net.ip.diff is that - # net.ip.subtract will always return the same type as its "ip" - # parameter. Its implementation takes the "delta" parameter, - # coerces it to be the same type as the "ip" paramter, negates it - # (using two's complement), and then adds it to "ip". - subtract = delta: ip: - let + # subtract :: (ip | mac | integer) -> ip -> ip + # + # net.ip.subtract is also the reverse of net.ip.add: + # + # net.ip.subtract a (net.ip.add a b) = b + # net.ip.subtract b (net.ip.add a b) = a + # + # The difference between net.ip.subtract and net.ip.diff is that + # net.ip.subtract will always return the same type as its "ip" + # parameter. Its implementation takes the "delta" parameter, + # coerces it to be the same type as the "ip" paramter, negates it + # (using two's complement), and then adds it to "ip". + subtract = delta: ip: let function = "net.ip.subtract"; delta' = typechecks.numeric function "delta" delta; ip' = typechecks.ip function "ip" ip; in - builders.ip (implementations.ip.subtract delta' ip'); - }; + builders.ip (implementations.ip.subtract delta' ip'); + }; - mac = { - - # add :: (ip | mac | integer) -> mac -> mac - # - # Examples: - # - # Adding integer to MAC: - # > net.mac.add 100 "fe:ed:fa:ce:f0:0d" - # "fe:ed:fa:ce:f0:71" - # - # Adding IPv4 to MAC: - # > net.mac.add "127.0.0.1" "fe:ed:fa:ce:f0:0d" - # "fe:ee:79:ce:f0:0e" - # - # Adding IPv6 to MAC: - # > net.mac.add "::cafe:beef" "fe:ed:fa:ce:f0:0d" - # "fe:ee:c5:cd:aa:cb - # - # Adding MAC to MAC: - # > net.mac.add "fe:ed:fa:00:00:00" "00:00:00:ce:f0:0d" - # "fe:ed:fa:ce:f0:0d" - add = delta: mac: - let + mac = { + # add :: (ip | mac | integer) -> mac -> mac + # + # Examples: + # + # Adding integer to MAC: + # > net.mac.add 100 "fe:ed:fa:ce:f0:0d" + # "fe:ed:fa:ce:f0:71" + # + # Adding IPv4 to MAC: + # > net.mac.add "127.0.0.1" "fe:ed:fa:ce:f0:0d" + # "fe:ee:79:ce:f0:0e" + # + # Adding IPv6 to MAC: + # > net.mac.add "::cafe:beef" "fe:ed:fa:ce:f0:0d" + # "fe:ee:c5:cd:aa:cb + # + # Adding MAC to MAC: + # > net.mac.add "fe:ed:fa:00:00:00" "00:00:00:ce:f0:0d" + # "fe:ed:fa:ce:f0:0d" + add = delta: mac: let function = "net.mac.add"; delta' = typechecks.numeric function "delta" delta; mac' = typechecks.mac function "mac" mac; in - builders.mac (implementations.mac.add delta' mac'); + builders.mac (implementations.mac.add delta' mac'); - # diff :: mac -> mac -> integer - # - # net.mac.diff is the reverse of net.mac.add: - # - # net.mac.diff (net.mac.add a b) a = b - # net.mac.diff (net.mac.add a b) b = a - # - # The difference between net.mac.diff and net.mac.subtract is that - # net.mac.diff will always return an integer. - diff = minuend: subtrahend: - let + # diff :: mac -> mac -> integer + # + # net.mac.diff is the reverse of net.mac.add: + # + # net.mac.diff (net.mac.add a b) a = b + # net.mac.diff (net.mac.add a b) b = a + # + # The difference between net.mac.diff and net.mac.subtract is that + # net.mac.diff will always return an integer. + diff = minuend: subtrahend: let function = "net.mac.diff"; minuend' = typechecks.mac function "minuend" minuend; subtrahend' = typechecks.mac function "subtrahend" subtrahend; in - implementations.mac.diff minuend' subtrahend'; + implementations.mac.diff minuend' subtrahend'; - # subtract :: (ip | mac | integer) -> mac -> mac - # - # net.mac.subtract is also the reverse of net.ip.add: - # - # net.mac.subtract a (net.mac.add a b) = b - # net.mac.subtract b (net.mac.add a b) = a - # - # The difference between net.mac.subtract and net.mac.diff is that - # net.mac.subtract will always return a MAC address. - subtract = delta: mac: - let + # subtract :: (ip | mac | integer) -> mac -> mac + # + # net.mac.subtract is also the reverse of net.ip.add: + # + # net.mac.subtract a (net.mac.add a b) = b + # net.mac.subtract b (net.mac.add a b) = a + # + # The difference between net.mac.subtract and net.mac.diff is that + # net.mac.subtract will always return a MAC address. + subtract = delta: mac: let function = "net.mac.subtract"; delta' = typechecks.numeric function "delta" delta; mac' = typechecks.mac function "mac" mac; in - builders.mac (implementations.mac.subtract delta' mac'); - }; + builders.mac (implementations.mac.subtract delta' mac'); + }; - cidr = { - # add :: (ip | mac | integer) -> cidr -> cidr - # - # > net.cidr.add 2 "127.0.0.0/8" - # "129.0.0.0/8" - # - # > net.cidr.add (-2) "127.0.0.0/8" - # "125.0.0.0/8" - add = delta: cidr: - let + cidr = { + # add :: (ip | mac | integer) -> cidr -> cidr + # + # > net.cidr.add 2 "127.0.0.0/8" + # "129.0.0.0/8" + # + # > net.cidr.add (-2) "127.0.0.0/8" + # "125.0.0.0/8" + add = delta: cidr: let function = "net.cidr.add"; delta' = typechecks.numeric function "delta" delta; cidr' = typechecks.cidr function "cidr" cidr; in - builders.cidr (implementations.cidr.add delta' cidr'); + builders.cidr (implementations.cidr.add delta' cidr'); - # child :: cidr -> cidr -> bool - # - # > net.cidr.child "10.10.10.0/24" "10.0.0.0/8" - # true - # - # > net.cidr.child "127.0.0.0/8" "10.0.0.0/8" - # false - child = subcidr: cidr: - let + # child :: cidr -> cidr -> bool + # + # > net.cidr.child "10.10.10.0/24" "10.0.0.0/8" + # true + # + # > net.cidr.child "127.0.0.0/8" "10.0.0.0/8" + # false + child = subcidr: cidr: let function = "net.cidr.child"; subcidr' = typechecks.cidr function "subcidr" subcidr; cidr' = typechecks.cidr function "cidr" cidr; in - implementations.cidr.child subcidr' cidr'; + implementations.cidr.child subcidr' cidr'; - # contains :: ip -> cidr -> bool - # - # > net.cidr.contains "127.0.0.1" "127.0.0.0/8" - # true - # - # > net.cidr.contains "127.0.0.1" "192.168.0.0/16" - # false - contains = ip: cidr: - let + # contains :: ip -> cidr -> bool + # + # > net.cidr.contains "127.0.0.1" "127.0.0.0/8" + # true + # + # > net.cidr.contains "127.0.0.1" "192.168.0.0/16" + # false + contains = ip: cidr: let function = "net.cidr.contains"; ip' = typechecks.ip function "ip" ip; cidr' = typechecks.cidr function "cidr" cidr; in - implementations.cidr.contains ip' cidr'; + implementations.cidr.contains ip' cidr'; - # capacity :: cidr -> integer - # - # > net.cidr.capacity "172.16.0.0/12" - # 1048576 - # - # > net.cidr.capacity "dead:cafe:beef::/96" - # 4294967296 - # - # > net.cidr.capacity "dead:cafe:beef::/48" (saturates to maxBound) - # 9223372036854775807 - capacity = cidr: - let + # capacity :: cidr -> integer + # + # > net.cidr.capacity "172.16.0.0/12" + # 1048576 + # + # > net.cidr.capacity "dead:cafe:beef::/96" + # 4294967296 + # + # > net.cidr.capacity "dead:cafe:beef::/48" (saturates to maxBound) + # 9223372036854775807 + capacity = cidr: let function = "net.cidr.capacity"; cidr' = typechecks.cidr function "cidr" cidr; in - implementations.cidr.capacity cidr'; + implementations.cidr.capacity cidr'; - # host :: (ip | mac | integer) -> cidr -> ip - # - # > net.cidr.host 10000 "10.0.0.0/8" - # 10.0.39.16 - # - # > net.cidr.host 10000 "dead:cafe:beef::/64" - # "dead:cafe:beef::2710" - # - # net.cidr.host "127.0.0.1" "dead:cafe:beef::/48" - # > "dead:cafe:beef::7f00:1" - # - # Inpsired by: - # https://www.terraform.io/docs/configuration/functions/cidrhost.html - host = hostnum: cidr: - let + # host :: (ip | mac | integer) -> cidr -> ip + # + # > net.cidr.host 10000 "10.0.0.0/8" + # 10.0.39.16 + # + # > net.cidr.host 10000 "dead:cafe:beef::/64" + # "dead:cafe:beef::2710" + # + # net.cidr.host "127.0.0.1" "dead:cafe:beef::/48" + # > "dead:cafe:beef::7f00:1" + # + # Inpsired by: + # https://www.terraform.io/docs/configuration/functions/cidrhost.html + host = hostnum: cidr: let function = "net.cidr.host"; hostnum' = typechecks.numeric function "hostnum" hostnum; cidr' = typechecks.cidr function "cidr" cidr; in - builders.ip (implementations.cidr.host hostnum' cidr'); + builders.ip (implementations.cidr.host hostnum' cidr'); - # length :: cidr -> integer - # - # > net.cidr.prefix "127.0.0.0/8" - # 8 - # - # > net.cidr.prefix "dead:cafe:beef::/48" - # 48 - length = cidr: - let + # length :: cidr -> integer + # + # > net.cidr.prefix "127.0.0.0/8" + # 8 + # + # > net.cidr.prefix "dead:cafe:beef::/48" + # 48 + length = cidr: let function = "net.cidr.length"; cidr' = typechecks.cidr function "cidr" cidr; in - implementations.cidr.length cidr'; + implementations.cidr.length cidr'; - # make :: integer -> ip -> cidr - # - # > net.cidr.make 24 "192.168.0.150" - # "192.168.0.0/24" - # - # > net.cidr.make 40 "dead:cafe:beef::feed:face:f00d" - # "dead:cafe:be00::/40" - make = length: base: - let + # make :: integer -> ip -> cidr + # + # > net.cidr.make 24 "192.168.0.150" + # "192.168.0.0/24" + # + # > net.cidr.make 40 "dead:cafe:beef::feed:face:f00d" + # "dead:cafe:be00::/40" + make = length: base: let function = "net.cidr.make"; length' = typechecks.int function "length" length; base' = typechecks.ip function "base" base; in - builders.cidr (implementations.cidr.make length' base'); + builders.cidr (implementations.cidr.make length' base'); - # netmask :: cidr -> ip - # - # > net.cidr.netmask "192.168.0.0/24" - # "255.255.255.0" - # - # > net.cidr.netmask "dead:cafe:beef::/64" - # "ffff:ffff:ffff:ffff::" - netmask = cidr: - let + # netmask :: cidr -> ip + # + # > net.cidr.netmask "192.168.0.0/24" + # "255.255.255.0" + # + # > net.cidr.netmask "dead:cafe:beef::/64" + # "ffff:ffff:ffff:ffff::" + netmask = cidr: let function = "net.cidr.netmask"; cidr' = typechecks.cidr function "cidr" cidr; in - builders.ip (implementations.cidr.netmask cidr'); + builders.ip (implementations.cidr.netmask cidr'); - # size :: cidr -> integer - # - # > net.cidr.prefix "127.0.0.0/8" - # 24 - # - # > net.cidr.prefix "dead:cafe:beef::/48" - # 80 - size = cidr: - let + # size :: cidr -> integer + # + # > net.cidr.prefix "127.0.0.0/8" + # 24 + # + # > net.cidr.prefix "dead:cafe:beef::/48" + # 80 + size = cidr: let function = "net.cidr.size"; cidr' = typechecks.cidr function "cidr" cidr; in - implementations.cidr.size cidr'; + implementations.cidr.size cidr'; - # subnet :: integer -> (ip | mac | integer) -> cidr -> cidr - # - # > net.cidr.subnet 4 2 "172.16.0.0/12" - # "172.18.0.0/16" - # - # > net.cidr.subnet 4 15 "10.1.2.0/24" - # "10.1.2.240/28" - # - # > net.cidr.subnet 16 162 "fd00:fd12:3456:7890::/56" - # "fd00:fd12:3456:7800:a200::/72" - # - # Inspired by: - # https://www.terraform.io/docs/configuration/functions/cidrsubnet.html - subnet = length: netnum: cidr: - let + # subnet :: integer -> (ip | mac | integer) -> cidr -> cidr + # + # > net.cidr.subnet 4 2 "172.16.0.0/12" + # "172.18.0.0/16" + # + # > net.cidr.subnet 4 15 "10.1.2.0/24" + # "10.1.2.240/28" + # + # > net.cidr.subnet 16 162 "fd00:fd12:3456:7890::/56" + # "fd00:fd12:3456:7800:a200::/72" + # + # Inspired by: + # https://www.terraform.io/docs/configuration/functions/cidrsubnet.html + subnet = length: netnum: cidr: let function = "net.cidr.subnet"; length' = typechecks.int function "length" length; netnum' = typechecks.numeric function "netnum" netnum; cidr' = typechecks.cidr function "cidr" cidr; in - builders.cidr (implementations.cidr.subnet length' netnum' cidr'); - - }; - } // ({ - types = - let - - mkParsedOptionType = { name, description, parser, builder }: - let - normalize = def: def // { + builders.cidr (implementations.cidr.subnet length' netnum' cidr'); + }; + } + // { + types = let + mkParsedOptionType = { + name, + description, + parser, + builder, + }: let + normalize = def: + def + // { value = builder (parser def.value); }; - in + in lib.mkOptionType { inherit name description; check = x: builtins.isString x && parser x != null; merge = loc: defs: lib.mergeEqualOption loc (map normalize defs); }; - dependent-ip = type: cidr: - let - cidrs = - if builtins.isList cidr - then cidr - else [ cidr ]; - in - lib.types.addCheck type (i: lib.any (net.cidr.contains i) cidrs) // { + dependent-ip = type: cidr: let + cidrs = + if builtins.isList cidr + then cidr + else [cidr]; + in + lib.types.addCheck type (i: lib.any (net.cidr.contains i) cidrs) + // { description = type.description + " in ${builtins.concatStringsSep " or " cidrs}"; }; - dependent-cidr = type: cidr: - let - cidrs = - if builtins.isList cidr - then cidr - else [ cidr ]; - in - lib.types.addCheck type (i: lib.any (net.cidr.child i) cidrs) // { + dependent-cidr = type: cidr: let + cidrs = + if builtins.isList cidr + then cidr + else [cidr]; + in + lib.types.addCheck type (i: lib.any (net.cidr.child i) cidrs) + // { description = type.description + " in ${builtins.concatStringsSep " or " cidrs}"; }; - - in - rec { - + in rec { ip = mkParsedOptionType { name = "ip"; description = "IPv4 or IPv6 address"; @@ -422,46 +402,44 @@ let parser = parsers.mac; builder = builders.mac; }; - }; - } - ); + }; list = { - cons = a: b: [ a ] ++ b; + cons = a: b: [a] ++ b; }; - bit = - let - shift = n: x: - if n < 0 - then x * math.pow 2 (-n) - else - let - safeDiv = n: d: if d == 0 then 0 else n / d; - d = math.pow 2 n; - in - if x < 0 - then not (safeDiv (not x) d) - else safeDiv x d; + bit = let + shift = n: x: + if n < 0 + then x * math.pow 2 (-n) + else let + safeDiv = n: d: + if d == 0 + then 0 + else n / d; + d = math.pow 2 n; + in + if x < 0 + then not (safeDiv (not x) d) + else safeDiv x d; - left = n: shift (-n); + left = n: shift (-n); - right = shift; + right = shift; - and = builtins.bitAnd; + and = builtins.bitAnd; - or = builtins.bitOr; + or = builtins.bitOr; - xor = builtins.bitXor; + xor = builtins.bitXor; - not = xor (-1); + not = xor (-1); - mask = n: and (left n 1 - 1); - in - { - inherit left right and or xor not mask; - }; + mask = n: and (left n 1 - 1); + in { + inherit left right and or xor not mask; + }; math = rec { max = a: b: @@ -484,89 +462,88 @@ let else pow (x * x) (n / 2); }; - parsers = - let + parsers = let + # fmap :: (a -> b) -> parser a -> parser b + fmap = f: ma: bind ma (a: pure (f a)); - # fmap :: (a -> b) -> parser a -> parser b - fmap = f: ma: bind ma (a: pure (f a)); + # pure :: a -> parser a + pure = a: string: { + leftovers = string; + result = a; + }; - # pure :: a -> parser a - pure = a: string: { - leftovers = string; - result = a; + # liftA2 :: (a -> b -> c) -> parser a -> parser b -> parser c + liftA2 = f: ma: mb: bind ma (a: bind mb (b: pure (f a b))); + liftA3 = f: a: b: ap (liftA2 f a b); + liftA4 = f: a: b: c: ap (liftA3 f a b c); + liftA5 = f: a: b: c: d: ap (liftA4 f a b c d); + liftA6 = f: a: b: c: d: e: ap (liftA5 f a b c d e); + + # ap :: parser (a -> b) -> parser a -> parser b + ap = liftA2 (a: a); + + # then_ :: parser a -> parser b -> parser b + then_ = liftA2 (a: b: b); + + # empty :: parser a + empty = string: null; + + # alt :: parser a -> parser a -> parser a + alt = left: right: string: let + result = left string; + in + if builtins.isNull result + then right string + else result; + + # guard :: bool -> parser {} + guard = condition: + if condition + then pure {} + else empty; + + # mfilter :: (a -> bool) -> parser a -> parser a + mfilter = f: parser: bind parser (a: then_ (guard (f a)) (pure a)); + + # some :: parser a -> parser [a] + some = v: liftA2 list.cons v (many v); + + # many :: parser a -> parser [a] + many = v: alt (some v) (pure []); + + # bind :: parser a -> (a -> parser b) -> parser b + bind = parser: f: string: let + a = parser string; + in + if builtins.isNull a + then null + else f a.result a.leftovers; + + # run :: parser a -> string -> maybe a + run = parser: string: let + result = parser string; + in + if builtins.isNull result || result.leftovers != "" + then null + else result.result; + + next = string: + if string == "" + then null + else { + leftovers = builtins.substring 1 (-1) string; + result = builtins.substring 0 1 string; }; - # liftA2 :: (a -> b -> c) -> parser a -> parser b -> parser c - liftA2 = f: ma: mb: bind ma (a: bind mb (b: pure (f a b))); - liftA3 = f: a: b: ap (liftA2 f a b); - liftA4 = f: a: b: c: ap (liftA3 f a b c); - liftA5 = f: a: b: c: d: ap (liftA4 f a b c d); - liftA6 = f: a: b: c: d: e: ap (liftA5 f a b c d e); - - # ap :: parser (a -> b) -> parser a -> parser b - ap = liftA2 (a: a); - - # then_ :: parser a -> parser b -> parser b - then_ = liftA2 (a: b: b); - - # empty :: parser a - empty = string: null; - - # alt :: parser a -> parser a -> parser a - alt = left: right: string: - let - result = left string; - in - if builtins.isNull result - then right string - else result; - - # guard :: bool -> parser {} - guard = condition: if condition then pure { } else empty; - - # mfilter :: (a -> bool) -> parser a -> parser a - mfilter = f: parser: bind parser (a: then_ (guard (f a)) (pure a)); - - # some :: parser a -> parser [a] - some = v: liftA2 list.cons v (many v); - - # many :: parser a -> parser [a] - many = v: alt (some v) (pure [ ]); - - # bind :: parser a -> (a -> parser b) -> parser b - bind = parser: f: string: - let - a = parser string; - in - if builtins.isNull a - then null - else f a.result a.leftovers; - - # run :: parser a -> string -> maybe a - run = parser: string: - let - result = parser string; - in - if builtins.isNull result || result.leftovers != "" - then null - else result.result; - - next = string: - if string == "" - then null - else { - leftovers = builtins.substring 1 (-1) string; - result = builtins.substring 0 1 string; - }; - - # Count how many characters were consumed by a parser - count = parser: string: - let - result = parser string; - in - if builtins.isNull result - then null - else result // { + # Count how many characters were consumed by a parser + count = parser: string: let + result = parser string; + in + if builtins.isNull result + then null + else + result + // { result = { inherit (result) result; count = with result; @@ -574,46 +551,49 @@ let }; }; - # Limit the parser to n characters at most - limit = n: parser: - fmap (a: a.result) (mfilter (a: a.count <= n) (count parser)); + # Limit the parser to n characters at most + limit = n: parser: + fmap (a: a.result) (mfilter (a: a.count <= n) (count parser)); - # Ensure the parser consumes exactly n characters - exactly = n: parser: - fmap (a: a.result) (mfilter (a: a.count == n) (count parser)); + # Ensure the parser consumes exactly n characters + exactly = n: parser: + fmap (a: a.result) (mfilter (a: a.count == n) (count parser)); - char = c: bind next (c': guard (c == c')); + char = c: bind next (c': guard (c == c')); - string = css: - if css == "" - then pure { } - else - let - c = builtins.substring 0 1 css; - cs = builtins.substring 1 (-1) css; - in - then_ (char c) (string cs); + string = css: + if css == "" + then pure {} + else let + c = builtins.substring 0 1 css; + cs = builtins.substring 1 (-1) css; + in + then_ (char c) (string cs); - digit = set: bind next ( - c: then_ + digit = set: + bind next ( + c: + then_ (guard (builtins.hasAttr c set)) (pure (builtins.getAttr c set)) ); - decimalDigits = { - "0" = 0; - "1" = 1; - "2" = 2; - "3" = 3; - "4" = 4; - "5" = 5; - "6" = 6; - "7" = 7; - "8" = 8; - "9" = 9; - }; + decimalDigits = { + "0" = 0; + "1" = 1; + "2" = 2; + "3" = 3; + "4" = 4; + "5" = 5; + "6" = 6; + "7" = 7; + "8" = 8; + "9" = 9; + }; - hexadecimalDigits = decimalDigits // { + hexadecimalDigits = + decimalDigits + // { "a" = 10; "b" = 11; "c" = 12; @@ -628,328 +608,293 @@ let "F" = 15; }; - fromDecimalDigits = builtins.foldl' (a: c: a * 10 + c) 0; - fromHexadecimalDigits = builtins.foldl' (a: bit.or (bit.left 4 a)) 0; + fromDecimalDigits = builtins.foldl' (a: c: a * 10 + c) 0; + fromHexadecimalDigits = builtins.foldl' (a: bit.or (bit.left 4 a)) 0; - # disallow leading zeros - decimal = bind (digit decimalDigits) ( - n: + # disallow leading zeros + decimal = bind (digit decimalDigits) ( + n: if n == 0 then pure 0 else fmap - (ns: fromDecimalDigits (list.cons n ns)) - (many (digit decimalDigits)) - ); + (ns: fromDecimalDigits (list.cons n ns)) + (many (digit decimalDigits)) + ); - hexadecimal = fmap fromHexadecimalDigits (some (digit hexadecimalDigits)); + hexadecimal = fmap fromHexadecimalDigits (some (digit hexadecimalDigits)); - ipv4 = - let - dot = char "."; + ipv4 = let + dot = char "."; - octet = mfilter (n: n < 256) decimal; + octet = mfilter (n: n < 256) decimal; - octet' = then_ dot octet; + octet' = then_ dot octet; - fromOctets = a: b: c: d: { - ipv4 = bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 a) b)) c)) d; + fromOctets = a: b: c: d: { + ipv4 = bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 a) b)) c)) d; + }; + in + liftA4 fromOctets octet octet' octet' octet'; + + # This is more or less a literal translation of + # https://hackage.haskell.org/package/ip/docs/src/Net.IPv6.html#parser + ipv6 = let + colon = char ":"; + + hextet = limit 4 hexadecimal; + + fromHextets = hextets: + if builtins.length hextets != 8 + then empty + else let + a = builtins.elemAt hextets 0; + b = builtins.elemAt hextets 1; + c = builtins.elemAt hextets 2; + d = builtins.elemAt hextets 3; + e = builtins.elemAt hextets 4; + f = builtins.elemAt hextets 5; + g = builtins.elemAt hextets 6; + h = builtins.elemAt hextets 7; + in + pure { + ipv6 = { + a = bit.or (bit.left 16 a) b; + b = bit.or (bit.left 16 c) d; + c = bit.or (bit.left 16 e) f; + d = bit.or (bit.left 16 g) h; + }; }; - in - liftA4 fromOctets octet octet' octet' octet'; - # This is more or less a literal translation of - # https://hackage.haskell.org/package/ip/docs/src/Net.IPv6.html#parser - ipv6 = - let - colon = char ":"; + ipv4' = + fmap + ( + address: let + upper = bit.right 16 address.ipv4; + lower = bit.mask 16 address.ipv4; + in [upper lower] + ) + ipv4; - hextet = limit 4 hexadecimal; - - fromHextets = hextets: - if builtins.length hextets != 8 - then empty - else - let - a = builtins.elemAt hextets 0; - b = builtins.elemAt hextets 1; - c = builtins.elemAt hextets 2; - d = builtins.elemAt hextets 3; - e = builtins.elemAt hextets 4; - f = builtins.elemAt hextets 5; - g = builtins.elemAt hextets 6; - h = builtins.elemAt hextets 7; - in - pure { - ipv6 = { - a = bit.or (bit.left 16 a) b; - b = bit.or (bit.left 16 c) d; - c = bit.or (bit.left 16 e) f; - d = bit.or (bit.left 16 g) h; - }; - }; - - ipv4' = fmap + part = n: let + n' = n + 1; + hex = + liftA2 list.cons hextet + ( + then_ colon ( - address: - let - upper = bit.right 16 address.ipv4; - lower = bit.mask 16 address.ipv4; - in - [ upper lower ] + alt + (then_ colon (doubleColon n')) + (part n') ) - ipv4; + ); + in + if n == 7 + then fmap (a: [a]) hextet + else if n == 6 + then alt ipv4' hex + else hex; - part = n: - let - n' = n + 1; - hex = liftA2 list.cons hextet - ( - then_ colon - ( - alt - (then_ colon (doubleColon n')) - (part n') - ) - ); - in - if n == 7 - then fmap (a: [ a ]) hextet - else - if n == 6 - then alt ipv4' hex - else hex; + doubleColon = n: + bind (alt afterDoubleColon (pure [])) ( + rest: let + missing = 8 - n - builtins.length rest; + in + if missing < 0 + then empty + else pure (builtins.genList (_: 0) missing ++ rest) + ); - doubleColon = n: - bind (alt afterDoubleColon (pure [ ])) ( - rest: - let - missing = 8 - n - builtins.length rest; - in - if missing < 0 - then empty - else pure (builtins.genList (_: 0) missing ++ rest) - ); - - afterDoubleColon = - alt ipv4' - ( - liftA2 list.cons hextet - ( - alt - (then_ colon afterDoubleColon) - (pure [ ]) - ) - ); - - in - bind + afterDoubleColon = + alt ipv4' + ( + liftA2 list.cons hextet ( alt - ( - then_ - (string "::") - (doubleColon 0) - ) - (part 0) + (then_ colon afterDoubleColon) + (pure []) ) - fromHextets; - - cidrv4 = - liftA2 - (base: length: implementations.cidr.make length base) - ipv4 - (then_ (char "/") (mfilter (n: n <= 32) decimal)); - - cidrv6 = - liftA2 - (base: length: implementations.cidr.make length base) - ipv6 - (then_ (char "/") (mfilter (n: n <= 128) decimal)); - - mac = - let - colon = char ":"; - - octet = exactly 2 hexadecimal; - - octet' = then_ colon octet; - - fromOctets = a: b: c: d: e: f: { - mac = bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 a) b)) c)) d)) e)) f; - }; - in - liftA6 fromOctets octet octet' octet' octet' octet' octet'; - + ); in - { - ipv4 = run ipv4; - ipv6 = run ipv6; - ip = run (alt ipv4 ipv6); - cidrv4 = run cidrv4; - cidrv6 = run cidrv6; - cidr = run (alt cidrv4 cidrv6); - mac = run mac; - numeric = run (alt (alt ipv4 ipv6) mac); - }; + bind + ( + alt + ( + then_ + (string "::") + (doubleColon 0) + ) + (part 0) + ) + fromHextets; - builders = - let + cidrv4 = + liftA2 + (base: length: implementations.cidr.make length base) + ipv4 + (then_ (char "/") (mfilter (n: n <= 32) decimal)); - ipv4 = address: - let - abcd = address.ipv4; - abc = bit.right 8 abcd; - ab = bit.right 8 abc; - a = bit.right 8 ab; - b = bit.mask 8 ab; - c = bit.mask 8 abc; - d = bit.mask 8 abcd; - in - builtins.concatStringsSep "." (map toString [ a b c d ]); + cidrv6 = + liftA2 + (base: length: implementations.cidr.make length base) + ipv6 + (then_ (char "/") (mfilter (n: n <= 128) decimal)); - # This is more or less a literal translation of - # https://hackage.haskell.org/package/ip/docs/src/Net.IPv6.html#encode - ipv6 = address: - let + mac = let + colon = char ":"; - digits = "0123456789abcdef"; + octet = exactly 2 hexadecimal; - toHexString = n: - let - rest = bit.right 4 n; - current = bit.mask 4 n; - prefix = - if rest == 0 - then "" - else toHexString rest; - in - "${prefix}${builtins.substring current 1 digits}"; - - in - if (with address.ipv6; a == 0 && b == 0 && c == 0 && d > 65535) - then "::${ipv4 { ipv4 = address.ipv6.d; }}" - else - if (with address.ipv6; a == 0 && b == 0 && c == 65535) - then "::ffff:${ipv4 { ipv4 = address.ipv6.d; }}" - else - let - - a = bit.right 16 address.ipv6.a; - b = bit.mask 16 address.ipv6.a; - c = bit.right 16 address.ipv6.b; - d = bit.mask 16 address.ipv6.b; - e = bit.right 16 address.ipv6.c; - f = bit.mask 16 address.ipv6.c; - g = bit.right 16 address.ipv6.d; - h = bit.mask 16 address.ipv6.d; - - hextets = [ a b c d e f g h ]; - - # calculate the position and size of the longest sequence of - # zeroes within the list of hextets - longest = - let - go = i: current: best: - if i < builtins.length hextets - then - let - n = builtins.elemAt hextets i; - - current' = - if n == 0 - then - if builtins.isNull current - then { - size = 1; - position = i; - } - else current // { - size = current.size + 1; - } - else null; - - best' = - if n == 0 - then - if builtins.isNull best - then current' - else - if current'.size > best.size - then current' - else best - else best; - in - go (i + 1) current' best' - else best; - in - go 0 null null; - - format = hextets: - builtins.concatStringsSep ":" (map toHexString hextets); - in - if builtins.isNull longest - then format hextets - else - let - sublist = i: length: xs: - map - (builtins.elemAt xs) - (builtins.genList (x: x + i) length); - - end = longest.position + longest.size; - - before = sublist 0 longest.position hextets; - - after = sublist end (builtins.length hextets - end) hextets; - in - "${format before}::${format after}"; - - ip = address: - if address ? ipv4 - then ipv4 address - else ipv6 address; - - cidrv4 = cidr: - "${ipv4 cidr.base}/${toString cidr.length}"; - - cidrv6 = cidr: - "${ipv6 cidr.base}/${toString cidr.length}"; - - cidr = cidr: - "${ip cidr.base}/${toString cidr.length}"; - - mac = address: - let - digits = "0123456789abcdef"; - octet = n: - let - upper = bit.right 4 n; - lower = bit.mask 4 n; - in - "${builtins.substring upper 1 digits}${builtins.substring lower 1 digits}"; - in - let - a = bit.mask 8 (bit.right 40 address.mac); - b = bit.mask 8 (bit.right 32 address.mac); - c = bit.mask 8 (bit.right 24 address.mac); - d = bit.mask 8 (bit.right 16 address.mac); - e = bit.mask 8 (bit.right 8 address.mac); - f = bit.mask 8 (bit.right 0 address.mac); - in - "${octet a}:${octet b}:${octet c}:${octet d}:${octet e}:${octet f}"; + octet' = then_ colon octet; + fromOctets = a: b: c: d: e: f: { + mac = bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 (bit.or (bit.left 8 a) b)) c)) d)) e)) f; + }; in - { - inherit ipv4 ipv6 ip cidrv4 cidrv6 cidr mac; - }; + liftA6 fromOctets octet octet' octet' octet' octet' octet'; + in { + ipv4 = run ipv4; + ipv6 = run ipv6; + ip = run (alt ipv4 ipv6); + cidrv4 = run cidrv4; + cidrv6 = run cidrv6; + cidr = run (alt cidrv4 cidrv6); + mac = run mac; + numeric = run (alt (alt ipv4 ipv6) mac); + }; + + builders = let + ipv4 = address: let + abcd = address.ipv4; + abc = bit.right 8 abcd; + ab = bit.right 8 abc; + a = bit.right 8 ab; + b = bit.mask 8 ab; + c = bit.mask 8 abc; + d = bit.mask 8 abcd; + in + builtins.concatStringsSep "." (map toString [a b c d]); + + # This is more or less a literal translation of + # https://hackage.haskell.org/package/ip/docs/src/Net.IPv6.html#encode + ipv6 = address: let + digits = "0123456789abcdef"; + + toHexString = n: let + rest = bit.right 4 n; + current = bit.mask 4 n; + prefix = + if rest == 0 + then "" + else toHexString rest; + in "${prefix}${builtins.substring current 1 digits}"; + in + if (with address.ipv6; a == 0 && b == 0 && c == 0 && d > 65535) + then "::${ipv4 {ipv4 = address.ipv6.d;}}" + else if (with address.ipv6; a == 0 && b == 0 && c == 65535) + then "::ffff:${ipv4 {ipv4 = address.ipv6.d;}}" + else let + a = bit.right 16 address.ipv6.a; + b = bit.mask 16 address.ipv6.a; + c = bit.right 16 address.ipv6.b; + d = bit.mask 16 address.ipv6.b; + e = bit.right 16 address.ipv6.c; + f = bit.mask 16 address.ipv6.c; + g = bit.right 16 address.ipv6.d; + h = bit.mask 16 address.ipv6.d; + + hextets = [a b c d e f g h]; + + # calculate the position and size of the longest sequence of + # zeroes within the list of hextets + longest = let + go = i: current: best: + if i < builtins.length hextets + then let + n = builtins.elemAt hextets i; + + current' = + if n == 0 + then + if builtins.isNull current + then { + size = 1; + position = i; + } + else + current + // { + size = current.size + 1; + } + else null; + + best' = + if n == 0 + then + if builtins.isNull best + then current' + else if current'.size > best.size + then current' + else best + else best; + in + go (i + 1) current' best' + else best; + in + go 0 null null; + + format = hextets: + builtins.concatStringsSep ":" (map toHexString hextets); + in + if builtins.isNull longest + then format hextets + else let + sublist = i: length: xs: + map + (builtins.elemAt xs) + (builtins.genList (x: x + i) length); + + end = longest.position + longest.size; + + before = sublist 0 longest.position hextets; + + after = sublist end (builtins.length hextets - end) hextets; + in "${format before}::${format after}"; + + ip = address: + if address ? ipv4 + then ipv4 address + else ipv6 address; + + cidrv4 = cidr: "${ipv4 cidr.base}/${toString cidr.length}"; + + cidrv6 = cidr: "${ipv6 cidr.base}/${toString cidr.length}"; + + cidr = cidr: "${ip cidr.base}/${toString cidr.length}"; + + mac = address: let + digits = "0123456789abcdef"; + octet = n: let + upper = bit.right 4 n; + lower = bit.mask 4 n; + in "${builtins.substring upper 1 digits}${builtins.substring lower 1 digits}"; + in let + a = bit.mask 8 (bit.right 40 address.mac); + b = bit.mask 8 (bit.right 32 address.mac); + c = bit.mask 8 (bit.right 24 address.mac); + d = bit.mask 8 (bit.right 16 address.mac); + e = bit.mask 8 (bit.right 8 address.mac); + f = bit.mask 8 (bit.right 0 address.mac); + in "${octet a}:${octet b}:${octet c}:${octet d}:${octet e}:${octet f}"; + in { + inherit ipv4 ipv6 ip cidrv4 cidrv6 cidr mac; + }; arithmetic = rec { # or :: (ip | mac | integer) -> (ip | mac | integer) -> (ip | mac | integer) - or = a_: b: - let - a = coerce b a_; - in + or = a_: b: let + a = coerce b a_; + in if a ? ipv6 then { ipv6 = { @@ -970,10 +915,9 @@ let else bit.or a b; # and :: (ip | mac | integer) -> (ip | mac | integer) -> (ip | mac | integer) - and = a_: b: - let - a = coerce b a_; - in + and = a_: b: let + a = coerce b a_; + in if a ? ipv6 then { ipv6 = { @@ -1015,33 +959,29 @@ let else bit.not a; # add :: (ip | mac | integer) -> (ip | mac | integer) -> (ip | mac | integer) - add = - let - split = a: { - fst = bit.mask 32 (bit.right 32 a); - snd = bit.mask 32 a; - }; + add = let + split = a: { + fst = bit.mask 32 (bit.right 32 a); + snd = bit.mask 32 a; + }; + in + a_: b: let + a = coerce b a_; in - a_: b: - let - a = coerce b a_; - in if a ? ipv6 - then - let - a' = split (a.ipv6.a + b.ipv6.a + b'.fst); - b' = split (a.ipv6.b + b.ipv6.b + c'.fst); - c' = split (a.ipv6.c + b.ipv6.c + d'.fst); - d' = split (a.ipv6.d + b.ipv6.d); - in - { - ipv6 = { - a = a'.snd; - b = b'.snd; - c = c'.snd; - d = d'.snd; - }; - } + then let + a' = split (a.ipv6.a + b.ipv6.a + b'.fst); + b' = split (a.ipv6.b + b.ipv6.b + c'.fst); + c' = split (a.ipv6.c + b.ipv6.c + d'.fst); + d' = split (a.ipv6.d + b.ipv6.d); + in { + ipv6 = { + a = a'.snd; + b = b'.snd; + c = c'.snd; + d = d'.snd; + }; + } else if a ? ipv4 then { ipv4 = bit.mask 32 (a.ipv4 + b.ipv4); @@ -1056,12 +996,11 @@ let subtract = a: b: add (add 1 (not (coerce b a))) b; # diff :: (ip | mac | integer) -> (ip | mac | integer) -> (ipv6 | integer) - diff = a: b: - let - toIPv6 = coerce ({ ipv6.a = 0; }); - result = (subtract b (toIPv6 a)).ipv6; - max32 = bit.left 32 1 - 1; - in + diff = a: b: let + toIPv6 = coerce {ipv6.a = 0;}; + result = (subtract b (toIPv6 a)).ipv6; + max32 = bit.left 32 1 - 1; + in if result.a == 0 && result.b == 0 && bit.right 31 result.c == 0 || result.a == max32 && result.b == max32 && bit.right 31 result.c == 1 then bit.or (bit.left 32 result.c) result.d else { @@ -1072,36 +1011,33 @@ let left = i: right (-i); # right :: integer -> (ip | mac | integer) -> (ip | mac | integer) - right = - let - step = i: x: { - _1 = bit.mask 32 (bit.right (i + 96) x); - _2 = bit.mask 32 (bit.right (i + 64) x); - _3 = bit.mask 32 (bit.right (i + 32) x); - _4 = bit.mask 32 (bit.right i x); - _5 = bit.mask 32 (bit.right (i - 32) x); - _6 = bit.mask 32 (bit.right (i - 64) x); - _7 = bit.mask 32 (bit.right (i - 96) x); - }; - ors = builtins.foldl' bit.or 0; - in + right = let + step = i: x: { + _1 = bit.mask 32 (bit.right (i + 96) x); + _2 = bit.mask 32 (bit.right (i + 64) x); + _3 = bit.mask 32 (bit.right (i + 32) x); + _4 = bit.mask 32 (bit.right i x); + _5 = bit.mask 32 (bit.right (i - 32) x); + _6 = bit.mask 32 (bit.right (i - 64) x); + _7 = bit.mask 32 (bit.right (i - 96) x); + }; + ors = builtins.foldl' bit.or 0; + in i: x: if x ? ipv6 - then - let - a' = step i x.ipv6.a; - b' = step i x.ipv6.b; - c' = step i x.ipv6.c; - d' = step i x.ipv6.d; - in - { - ipv6 = { - a = ors [ a'._4 b'._3 c'._2 d'._1 ]; - b = ors [ a'._5 b'._4 c'._3 d'._2 ]; - c = ors [ a'._6 b'._5 c'._4 d'._3 ]; - d = ors [ a'._7 b'._6 c'._5 d'._4 ]; - }; - } + then let + a' = step i x.ipv6.a; + b' = step i x.ipv6.b; + c' = step i x.ipv6.c; + d' = step i x.ipv6.d; + in { + ipv6 = { + a = ors [a'._4 b'._3 c'._2 d'._1]; + b = ors [a'._5 b'._4 c'._3 d'._2]; + c = ors [a'._6 b'._5 c'._4 d'._3]; + d = ors [a'._7 b'._6 c'._5 d'._4]; + }; + } else if x ? ipv4 then { ipv4 = bit.mask 32 (bit.right i x.ipv4); @@ -1180,21 +1116,20 @@ let else { mac = bit.mask 48 value; } - else - if value ? ipv6 - then - builtins.foldl' bit.or 0 - [ - (bit.left 96 value.ipv6.a) - (bit.left 64 value.ipv6.b) - (bit.left 32 value.ipv6.c) - value.ipv6.d - ] - else if value ? ipv4 - then value.ipv4 - else if value ? mac - then value.mac - else value; + else if value ? ipv6 + then + builtins.foldl' bit.or 0 + [ + (bit.left 96 value.ipv6.a) + (bit.left 64 value.ipv6.b) + (bit.left 32 value.ipv6.c) + value.ipv6.d + ] + else if value ? ipv4 + then value.ipv4 + else if value ? mac + then value.mac + else value; }; implementations = { @@ -1222,20 +1157,17 @@ let cidr = rec { # add :: (ip | mac | integer) -> cidr -> cidr - add = delta: cidr: - let - size' = size cidr; - in - { - base = arithmetic.left size' (arithmetic.add delta (arithmetic.right size' cidr.base)); - inherit (cidr) length; - }; + add = delta: cidr: let + size' = size cidr; + in { + base = arithmetic.left size' (arithmetic.add delta (arithmetic.right size' cidr.base)); + inherit (cidr) length; + }; # capacity :: cidr -> integer - capacity = cidr: - let - size' = size cidr; - in + capacity = cidr: let + size' = size cidr; + in if size' > 62 then 9223372036854775807 # maxBound to prevent overflow else bit.left size' 1; @@ -1248,10 +1180,9 @@ let contains = ip: cidr: host 0 (make cidr.length ip) == host 0 cidr; # host :: (ip | mac | integer) -> cidr -> ip - host = index: cidr: - let - index' = arithmetic.coerce cidr.base index; - in + host = index: cidr: let + index' = arithmetic.coerce cidr.base index; + in arithmetic.or (arithmetic.shadow cidr.length index') cidr.base; # length :: cidr -> integer @@ -1261,63 +1192,71 @@ let netmask = cidr: arithmetic.coshadow cidr.length (arithmetic.coerce cidr.base (-1)); # size :: cidr -> integer - size = cidr: (if cidr.base ? ipv6 then 128 else 32) - cidr.length; + size = cidr: + ( + if cidr.base ? ipv6 + then 128 + else 32 + ) + - cidr.length; # subnet :: integer -> (ip | mac | integer) -> cidr -> cidr - subnet = length: index: cidr: - let - length' = cidr.length + length; - index' = arithmetic.coerce cidr.base index; - size = (if cidr.base ? ipv6 then 128 else 32) - length'; - in + subnet = length: index: cidr: let + length' = cidr.length + length; + index' = arithmetic.coerce cidr.base index; + size = + ( + if cidr.base ? ipv6 + then 128 + else 32 + ) + - length'; + in make length' (host (arithmetic.left size index') cidr); # make :: integer -> ip -> cidr - make = length: base: - let - length' = math.clamp 0 (if base ? ipv6 then 128 else 32) length; - in - { - base = arithmetic.coshadow length' base; - length = length'; - }; + make = length: base: let + length' = + math.clamp 0 ( + if base ? ipv6 + then 128 + else 32 + ) + length; + in { + base = arithmetic.coshadow length' base; + length = length'; + }; }; }; - typechecks = - let - - fail = description: function: argument: - builtins.throw "${function}: ${argument} parameter must be ${description}"; - - meta = parser: description: function: argument: input: - let - error = fail description function argument; - in - if !builtins.isString input - then error - else - let - result = parser input; - in - if builtins.isNull result - then error - else result; + typechecks = let + fail = description: function: argument: + builtins.throw "${function}: ${argument} parameter must be ${description}"; + meta = parser: description: function: argument: input: let + error = fail description function argument; in - { - int = function: argument: input: - if builtins.isInt input - then input - else fail "an integer" function argument; - ip = meta parsers.ip "an IPv4 or IPv6 address"; - cidr = meta parsers.cidr "an IPv4 or IPv6 address range in CIDR notation"; - mac = meta parsers.mac "a MAC address"; - numeric = function: argument: input: - if builtins.isInt input - then input - else meta parsers.numeric "an integer or IPv4, IPv6 or MAC address" function argument input; - }; - + if !builtins.isString input + then error + else let + result = parser input; + in + if builtins.isNull result + then error + else result; + in { + int = function: argument: input: + if builtins.isInt input + then input + else fail "an integer" function argument; + ip = meta parsers.ip "an IPv4 or IPv6 address"; + cidr = meta parsers.cidr "an IPv4 or IPv6 address range in CIDR notation"; + mac = meta parsers.mac "a MAC address"; + numeric = function: argument: input: + if builtins.isInt input + then input + else meta parsers.numeric "an integer or IPv4, IPv6 or MAC address" function argument input; + }; in -net + net