2
0

1 Commits

Author SHA1 Message Date
dan e8e0de0284 make tftpboot work on devices with old u-boot
Some devices have a U-boot variant that does not accept a third
parameter on the "bootm" command, meaning we can't override the dtb
in the bootloader so have to smush it back into the kernel image

This is WIP because
- still hardcodes MIPS
- doesn't work in QEMU

Based on
https://gti.telent.net/raboof/liminix/src/branch/tftp-old-uboot

Co-authored-by:  Arnout Engelen <arnout@bzzt.net>
2024-02-15 20:12:03 +00:00
1629 changed files with 1234 additions and 7605 deletions
+3 -38
View File
@@ -34,17 +34,13 @@ Upstream changes that have led to incompatible Liminix changes are:
2024-01-30
New port! Thanks to Arnout Engelen <arnout@bzzt.net>, Liminix
now runs on the TP-Link Archer AX23.
now runs on the TP-Link Archer AX23
2024-02-12
* We now build wifi drivers (mac80211) from the same kernel source as
* we now build wifi drivers (mac80211) from the same kernel source as
the running kernel, instead of using drivers from the linux-backports
project. This may be a regression on some devices that depend on
OpenWrt patches for wireless functionality: if you have a device that
used to work and now doesn't, refer to OpenWrt
package/kernel/mac80211/patches/ to see if there's something in there
that needs to be applied.
project.
* in general, we build kernel modules (e.g. for nftables) at the same
time as the kernel itself instead of expecting to be able to build
@@ -72,34 +68,3 @@ the old location to the new one before rebooting into the new system
The `output`, `mkoutputs` functions defined by ${serviceFns}
have been updated for the new location.
2024-02-16
New (or at least, previously unreported) port! Liminix now runs on the
Turris Omnia and has been serving my family's internet needs for most
of this week. Thanks to NGI0 Entrust and the NLnet Foundation for
sponsoring this development (and funding the hardware)
2024-02-21
New port! Thanks to Raito Bezarius, Liminix now runs on the Zyxel NWA50AX,
an MT7621 (MIPS EL) dual radio WiFi AP.
2024-04-29
The setup for using `levitate` has changed: now it accepts an entire
config fragment, not just a list of services. Hopefully this makes it
a bit more useful :-)
defaultProfile.packages = with pkgs; [
...
(levitate.override {
config = {
services = {
inherit (config.services) dhcpc sshd watchdog;
};
defaultProfile.packages = [ mtdutils ];
users.root.openssh.authorizedKeys.keys = secrets.root.keys;
};
})
];
-1084
View File
File diff suppressed because it is too large Load Diff
-20
View File
@@ -1,20 +0,0 @@
# This is for use with minicom, but needs you to configure it to
# use expect as its "Script program" instead of runscript. Try
# Ctrl+A O -> Filenames and paths -> D
log_user 0
log_file -a -open stderr
set f [open "result/boot.scr"]
send "version\r"
set timeout 60
while {[gets $f line] >= 0} {
puts stderr "next line $line\r"
puts stderr "waiting for prompt\r"
expect {
"ath>" {}
"BusyBox" { puts stderr "DONE"; exit 0 }
}
send "$line\r\n"
}
puts stderr "done\r\n"
close $f
+18 -40
View File
@@ -4,10 +4,6 @@ let
inherit (lib) mkOption mkEnableOption mdDoc types optional optionals;
in {
options.bordervm = {
keys = mkOption {
type = types.listOf types.str;
default = [ ];
};
l2tp = {
host = mkOption {
description = mdDoc ''
@@ -55,17 +51,18 @@ in {
<nixpkgs/nixos/modules/virtualisation/qemu-vm.nix>
];
config = {
boot.kernelParams = [ "loglevel=9" ];
boot.kernelParams = [
"loglevel=9"
];
systemd.services.pppoe =
let
conf = pkgs.writeText "kpppoed.toml" ''
interface_name = "eth1"
services = [ "myservice" ]
lns_ipaddr = "${cfg.l2tp.host}:${builtins.toString cfg.l2tp.port}"
ac_name = "kpppoed-1.0"
'';
in
{
let conf = pkgs.writeText "kpppoed.toml"
''
interface_name = "eth1"
services = [ "myservice" ]
lns_ipaddr = "${cfg.l2tp.host}:${builtins.toString cfg.l2tp.port}"
ac_name = "kpppoed-1.0"
'';
in {
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
serviceConfig = {
@@ -79,36 +76,24 @@ in {
};
};
services.openssh.enable = true;
services.dnsmasq = {
enable = true;
resolveLocalQueries = false;
settings = {
# domain-needed = true;
dhcp-range = [ "10.0.0.10,10.0.0.240" ];
interface = "eth1";
};
};
systemd.services.sshd.wantedBy = pkgs.lib.mkForce [ "multi-user.target" ];
virtualisation = {
qemu = {
networkingOptions = [ ];
options =
[ ]
++ optional cfg.ethernet.pci.enable "-device vfio-pci,host=${cfg.ethernet.pci.id}"
++ optionals cfg.ethernet.usb.enable [
networkingOptions = [];
options = [] ++
optional cfg.ethernet.pci.enable
"-device vfio-pci,host=${cfg.ethernet.pci.id}" ++
optionals cfg.ethernet.usb.enable [
"-device usb-ehci,id=ehci"
"-device usb-host,bus=ehci.0,vendorid=${cfg.ethernet.usb.vendor},productid=${cfg.ethernet.usb.product}"
]
++ [
] ++ [
"-nographic"
"-serial mon:stdio"
];
};
sharedDirectories = {
liminix = {
securityModel = "none";
source = builtins.toString ./.;
target = "/home/liminix/liminix";
};
@@ -123,7 +108,6 @@ in {
tufted
iptables
usbutils
busybox
];
security.sudo.wheelNeedsPassword = false;
networking = {
@@ -133,17 +117,11 @@ in {
useDHCP = false;
ipv4.addresses = [ { address = "10.0.0.1"; prefixLength = 24;}];
};
nat = {
enable = true;
internalInterfaces = [ "eth1" ];
externalInterface = "eth0";
};
};
users.users.liminix = {
isNormalUser = true;
uid = 1000;
extraGroups = [ "wheel" ];
openssh.authorizedKeys.keys = cfg.keys;
extraGroups = [ "wheel"];
};
services.getty.autologinUser = "liminix";
};
+2 -6
View File
@@ -1,12 +1,8 @@
{ ... }:
{...}:
{
bordervm = {
# ethernet.pci = { id = "01:00.0"; enable = true; };
ethernet.usb = {
vendor = "0x0bda";
product = "0x8153";
enable = true;
};
ethernet.usb = { vendor = "0x0bda"; product = "0x8153"; enable = true; };
l2tp = {
host = "l2tp.aa.net.uk";
};
+31 -38
View File
@@ -1,22 +1,17 @@
{
nixpkgs,
unstable,
liminix,
...
}:
nixpkgs
, unstable
, liminix
, ... }:
let
pkgs = (import nixpkgs { });
borderVmConf = ./bordervm.conf-example.nix;
inherit (builtins) map;
pkgs = (import nixpkgs {});
borderVmConf = ./bordervm.conf-example.nix;
inherit (pkgs.lib.attrsets) genAttrs;
devices = [
"gl-ar750"
"gl-mt300a"
"gl-mt300n-v2"
"qemu"
"qemu-aarch64"
"qemu-armv7l"
"gl-ar750" "gl-mt300n-v2" "gl-mt300a"
"qemu" "qemu-aarch64" "qemu-armv7l"
"tp-archer-ax23"
"zyxel-nwa50ax"
];
vanilla = ./vanilla-configuration.nix;
for-device = name:
@@ -27,35 +22,33 @@ let
}).outputs.default;
tests = import ./tests/ci.nix;
jobs =
(genAttrs devices for-device)
// tests
// {
buildEnv =
(import liminix {
inherit nixpkgs borderVmConf;
device = import (liminix + "/devices/qemu");
liminix-config = vanilla;
}).buildEnv;
(genAttrs devices for-device) //
tests //
{
buildEnv = (import liminix {
inherit nixpkgs borderVmConf;
device = import (liminix + "/devices/qemu");
liminix-config = vanilla;
}).buildEnv;
doc =
let
json =
(import liminix {
inherit nixpkgs borderVmConf;
device = import (liminix + "/devices/qemu");
liminix-config =
{ ... }:
{
let json =
(import liminix {
inherit nixpkgs borderVmConf;
device = import (liminix + "/devices/qemu");
liminix-config = {...} : {
imports = [ ./modules/all-modules.nix ];
};
}).outputs.optionsJson;
in
pkgs.stdenv.mkDerivation {
}).outputs.optionsJson;
installers = map (f: "system.outputs.${f}") [
"vmroot"
"mtdimage"
"ubimage"
];
inherit (pkgs.lib) concatStringsSep;
in pkgs.stdenv.mkDerivation {
name = "liminix-doc";
nativeBuildInputs = with pkgs; [
gnumake
sphinx
fennel
luaPackages.lyaml
gnumake sphinx fennel luaPackages.lyaml
];
src = ./.;
buildPhase = ''
+15 -35
View File
@@ -1,47 +1,35 @@
{
deviceName ? null,
device ? (import ./devices/${deviceName}),
liminix-config ? <liminix-config>,
nixpkgs ? <nixpkgs>,
borderVmConf ? ./bordervm.conf.nix,
imageType ? "primary",
device
, liminix-config ? <liminix-config>
, nixpkgs ? <nixpkgs>
, borderVmConf ? ./bordervm.conf.nix
}:
let
overlay = import ./overlay.nix;
pkgs = import nixpkgs (
device.system
// {
overlays = [ overlay ];
config = {
allowUnsupportedSystem = true; # mipsel
permittedInsecurePackages = [
"python-2.7.18.6" # kernel backports needs python <3
"python-2.7.18.7"
];
};
}
);
pkgs = import nixpkgs (device.system // {
overlays = [overlay];
config = {
allowUnsupportedSystem = true; # mipsel
permittedInsecurePackages = [
"python-2.7.18.6" # kernel backports needs python <3
"python-2.7.18.7"
];
};
});
eval = pkgs.lib.evalModules {
specialArgs = {
modulesPath = builtins.toString ./modules;
};
modules = [
{ _module.args = { inherit pkgs; inherit (pkgs) lim; }; }
./modules/hardware.nix
./modules/base.nix
./modules/busybox.nix
./modules/hostname.nix
./modules/kernel
device.module
liminix-config
./modules/s6
./modules/users.nix
./modules/outputs.nix
{
boot.imageType = imageType;
}
];
};
config = eval.config;
@@ -49,14 +37,7 @@ let
borderVm = ((import <nixpkgs/nixos/lib/eval-config.nix>) {
system = builtins.currentSystem;
modules = [
{
nixpkgs.overlays = [
(final: prev: {
go-l2tp = final.callPackage ./pkgs/go-l2tp {};
tufted = final.callPackage ./pkgs/tufted {};
})
];
}
({ ... } : { nixpkgs.overlays = [ overlay ]; })
(import ./bordervm-configuration.nix)
borderVmConf
];
@@ -87,7 +68,6 @@ in {
min-copy-closure
fennelrepl
lzma
lua
];
};
}
+3 -1
View File
@@ -73,7 +73,7 @@
MTK_INFRACFG = "y";
MTK_PMIC_WRAP = "y";
NVMEM_MTK_EFUSE="y";
MTK_EFUSE="y";
# MTK_HSDMA="y";
MTK_SCPSYS="y";
MTK_SCPSYS_PM_DOMAINS="y";
@@ -92,6 +92,7 @@
MEDIATEK_GE_PHY = "y";
# MEDIATEK_MT6577_AUXADC = "y";
# MEDIATEK_WATCHDOG = "y";
NET_MEDIATEK_SOC = "y";
NET_MEDIATEK_SOC_WED = "y";
NET_MEDIATEK_STAR_EMAC = "y"; # this enables REGMAP_MMIO
@@ -213,6 +214,7 @@
networkInterfaces =
let
inherit (config.system.service.network) link;
inherit (config.system.service) bridge;
in rec {
wan = link.build { ifname = "wan"; };
lan1 = link.build { ifname = "lan1"; };
+3 -8
View File
@@ -23,17 +23,12 @@
VIRTIO_BLK = "y";
VIRTIO_NET = "y";
};
conditionalConfig = {
WLAN= {
MAC80211_HWSIM = "m";
};
};
};
hardware =
let
mac80211 = pkgs.kmodloader.override {
inherit (config.system.outputs) kernel;
targets = ["mac80211_hwsim"];
mac80211 = pkgs.mac80211.override {
drivers = ["mac80211_hwsim"];
klibBuild = config.system.outputs.kernel.modulesupport;
};
in {
defaultOutput = "vmroot";
+4 -15
View File
@@ -74,7 +74,6 @@
mac80211 = pkgs.kmodloader.override {
targets = ["ath9k" "ath10k_pci"];
inherit (config.system.outputs) kernel;
dependencies = [ ath10k_cal_data ];
};
ath10k_cal_data =
let
@@ -92,6 +91,7 @@
'';
};
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs.liminix.networking) interface;
in {
imports = [
../../modules/network
@@ -124,23 +124,15 @@
networkInterfaces =
let inherit (config.system.service.network) link;
in {
lan = link.build {
ifname = "lan";
# devpath = "/devices/platform/ahb/19000000.eth";
devpath = "/devices/platform/ahb/1a000000.eth";
};
wan = link.build {
ifname = "wan";
devpath = "/devices/platform/ahb/19000000.eth";
# devpath = "/devices/platform/ahb/1a000000.eth";
};
lan = link.build { ifname = "eth0"; };
wan = link.build { ifname = "eth1"; };
wlan = link.build {
ifname = "wlan0";
dependencies = [ mac80211 ];
};
wlan5 = link.build {
ifname = "wlan1";
dependencies = [ ath10k_cal_data mac80211 ];
dependencies = [ mac80211 ath10k_cal_data ];
};
};
};
@@ -156,7 +148,6 @@
};
boot.tftp = {
loadAddress = lim.parseInt "0x00A00000";
appendDTB = true;
};
kernel = {
src = pkgs.pkgsBuildBuild.fetchurl {
@@ -170,8 +161,6 @@
# OpenWrt kernel patches
extraPatchPhase = ''
${openwrt.applyPatches.ath79}
sed -i.bak -e '\,include <linux/hw_random.h>,a #include <linux/gpio/driver.h>' drivers/net/wireless/ath/ath9k/ath9k.h # context reqd for next patch
patch -p1 < ${openwrt.src}/package/kernel/mac80211/patches/ath9k/552-ath9k-ahb_of.patch
'';
config = {
+17 -3
View File
@@ -45,6 +45,7 @@
module = { pkgs, config, lib, lim, ...}:
let
inherit (pkgs.liminix.networking) interface;
inherit (pkgs) openwrt;
mac80211 = pkgs.kmodloader.override {
targets = ["rt2800soc"];
@@ -89,6 +90,19 @@
let
inherit (config.system.service.network) link;
inherit (config.system.service) vlan;
inherit (pkgs.liminix.services) oneshot;
swconfig = oneshot {
name = "swconfig";
up = ''
PATH=${pkgs.swconfig}/bin:$PATH
swconfig dev switch0 set reset
swconfig dev switch0 set enable_vlan 1
swconfig dev switch0 vlan 1 set ports '1 2 3 4 6t'
swconfig dev switch0 vlan 2 set ports '0 6t'
swconfig dev switch0 set apply
'';
down = "${pkgs.swconfig}/bin/swconfig dev switch0 set reset";
};
in rec {
eth = link.build { ifname = "eth0"; };
# lan and wan ports are both behind a switch on eth0
@@ -96,11 +110,13 @@
ifname = "eth0.1";
primary = eth;
vid = "1";
dependencies = [swconfig eth];
};
wan = vlan.build {
ifname = "eth0.2";
primary = eth;
vid = "2";
dependencies = [swconfig eth];
};
wlan = link.build {
ifname = "wlan0";
@@ -110,8 +126,7 @@
};
boot.tftp = {
loadAddress = lim.parseInt "0x00A00000";
appendDTB = true;
};
};
kernel = {
src = pkgs.fetchurl {
@@ -121,7 +136,6 @@
};
extraPatchPhase = ''
${openwrt.applyPatches.ramips}
${openwrt.applyPatches.rt2x00}
'';
config = {
+2 -2
View File
@@ -38,6 +38,7 @@
module = { pkgs, config, lib, lim, ...}:
let
inherit (pkgs.liminix.networking) interface;
inherit (pkgs.liminix.services) oneshot;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) openwrt;
@@ -96,7 +97,7 @@
swconfig dev switch0 vlan 2 set ports '0 6t'
swconfig dev switch0 set apply
'';
down = "${pkgs.swconfig}/bin/swconfig dev switch0 set reset";
down = "swconfig dev switch0 set reset";
};
in rec {
eth = link.build { ifname = "eth0"; dependencies = [swconfig]; };
@@ -121,7 +122,6 @@
# 20MB seems to give enough room to uncompress the kernel
# without anything getting trodden on. 10MB was too small
loadAddress = lim.parseInt "0x1400000";
appendDTB = true;
};
kernel = {
+1 -1
View File
@@ -26,7 +26,7 @@
# this device is described by the "qemu" device
installer = "vmroot";
module = { config, lim, ... }: {
module = {pkgs, config, lim, ... }: {
imports = [
../../modules/arch/aarch64.nix
../families/qemu.nix
+1 -1
View File
@@ -24,7 +24,7 @@
'';
installer = "vmroot";
module = { config, lim, ... }: {
module = {pkgs, config, lim, ... }: {
imports = [
../../modules/arch/arm.nix
../families/qemu.nix
+1 -1
View File
@@ -36,7 +36,7 @@
in the Development manual.
'';
module = { config, lib, lim, ... }: {
module = {pkgs, config, lib, lim, ... }: {
imports = [
../../modules/arch/mipseb.nix
../families/qemu.nix
+1
View File
@@ -419,6 +419,7 @@
networkInterfaces =
let
inherit (config.system.service.network) link;
inherit (config.system.service) bridge;
in rec {
lan1 = link.build { ifname = "lan1"; };
lan2 = link.build { ifname = "lan2"; };
+3
View File
@@ -155,6 +155,8 @@
module = {pkgs, config, lib, lim, ... }:
let
openwrt = pkgs.openwrt;
inherit (lib) mkOption types;
inherit (pkgs.liminix.services) oneshot;
inherit (pkgs) liminix;
mtd_by_name_links = pkgs.liminix.services.oneshot rec {
@@ -356,6 +358,7 @@
networkInterfaces =
let
inherit (config.system.service.network) link;
inherit (config.system.service) bridge;
in rec {
en70000 = link.build {
# in armada-38x.dtsi this is eth0.
@@ -1,155 +0,0 @@
#include "mt7621.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
/ {
aliases {
label-mac-device = &gmac0;
};
};
&nand {
status = "okay";
mediatek,nmbm;
mediatek,bmt-max-ratio = <15>;
mediatek,bmt-max-reserved-blocks = <64>;
mediatek,bmt-remap-range =
<0x0 0x980000>,
<0x2980000 0x7800000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0x80000>;
read-only;
};
partition@80000 {
label = "u-boot-env";
reg = <0x80000 0x80000>;
read-only;
};
factory: partition@100000 {
label = "factory";
reg = <0x100000 0x80000>;
read-only;
};
partition@180000 {
label = "firmware_a";
reg = <0x180000 0x2800000>;
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "kernel_a";
reg = <0x0 0x800000>;
};
partition@400000 {
label = "ubi";
reg = <0x800000 0x2000000>;
};
};
partition@2980000 {
label = "firmware_b";
reg = <0x2980000 0x2800000>;
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "kernel_b";
reg = <0x0 0x800000>;
};
partition@400000 {
label = "ubi_b";
reg = <0x800000 0x2000000>;
};
};
partition@5180000 {
label = "rootfs_data";
reg = <0x5180000 0x1400000>;
};
partition@6580000 {
label = "logs";
reg = <0x6580000 0xd00000>;
};
partition@7280000 {
label = "vendor-myzyxel";
reg = <0x7280000 0x480000>;
read-only;
};
partition@7700000 {
label = "bootconfig";
reg = <0x7700000 0x80000>;
};
mrd: partition@7780000 {
label = "mrd";
reg = <0x7780000 0x80000>;
read-only;
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
macaddr_mrd_1fff8: macaddr@1fff8 {
reg = <0x1fff8 0x6>;
};
};
};
};
};
&pcie {
status = "okay";
};
&pcie1 {
wlan_5g: wifi@0,0 {
reg = <0x0 0 0 0 0>;
compatible = "mediatek,mt76";
mediatek,mtd-eeprom = <&factory 0x0>;
/* MAC-Address set in userspace */
};
};
&gmac0 {
nvmem-cells = <&macaddr_mrd_1fff8>;
nvmem-cell-names = "mac-address";
};
&switch0 {
ports {
port@4 {
status = "okay";
label = "lan";
};
};
};
&state_default {
gpio {
groups = "uart3";
function = "gpio";
};
};
@@ -1,155 +0,0 @@
#include "mt7621.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
/ {
aliases {
label-mac-device = &gmac0;
};
};
&nand {
status = "okay";
mediatek,nmbm;
mediatek,bmt-max-ratio = <15>;
mediatek,bmt-max-reserved-blocks = <64>;
mediatek,bmt-remap-range =
<0x0 0x980000>,
<0x2980000 0x7800000>;
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "u-boot";
reg = <0x0 0x80000>;
read-only;
};
partition@80000 {
label = "u-boot-env";
reg = <0x80000 0x80000>;
read-only;
};
factory: partition@100000 {
label = "factory";
reg = <0x100000 0x80000>;
read-only;
};
partition@2980000 {
label = "firmware_b";
reg = <0x2980000 0x2800000>;
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "kernel_b";
reg = <0x0 0x800000>;
};
partition@400000 {
label = "ubi";
reg = <0x800000 0x2000000>;
};
};
partition@180000 {
label = "firmware_a";
reg = <0x180000 0x2800000>;
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
partition@0 {
label = "kernel_a";
reg = <0x0 0x800000>;
};
partition@400000 {
label = "ubi_a";
reg = <0x800000 0x2000000>;
};
};
partition@5180000 {
label = "rootfs_data";
reg = <0x5180000 0x1400000>;
};
partition@6580000 {
label = "logs";
reg = <0x6580000 0xd00000>;
};
partition@7280000 {
label = "vendor-myzyxel";
reg = <0x7280000 0x480000>;
read-only;
};
partition@7700000 {
label = "bootconfig";
reg = <0x7700000 0x80000>;
};
mrd: partition@7780000 {
label = "mrd";
reg = <0x7780000 0x80000>;
read-only;
nvmem-layout {
compatible = "fixed-layout";
#address-cells = <1>;
#size-cells = <1>;
macaddr_mrd_1fff8: macaddr@1fff8 {
reg = <0x1fff8 0x6>;
};
};
};
};
};
&pcie {
status = "okay";
};
&pcie1 {
wlan_5g: wifi@0,0 {
reg = <0x0 0 0 0 0>;
compatible = "mediatek,mt76";
mediatek,mtd-eeprom = <&factory 0x0>;
/* MAC-Address set in userspace */
};
};
&gmac0 {
nvmem-cells = <&macaddr_mrd_1fff8>;
nvmem-cell-names = "mac-address";
};
&switch0 {
ports {
port@4 {
status = "okay";
label = "lan";
};
};
};
&state_default {
gpio {
groups = "uart3";
function = "gpio";
};
};
-365
View File
@@ -1,365 +0,0 @@
{
system = {
crossSystem = {
config = "mipsel-unknown-linux-musl";
gcc = {
abi = "32";
arch = "mips32"; # mips32r2?
};
};
};
description = ''
Zyxel NWA50AX
********************
Zyxel NWA50AX is quite close to the GL-MT300N-v2 "Mango" device, but it is based on the MT7621
chipset instead of the MT7628.
Installation
============
This device is pretty, but, due to its A/B capabilities, can be a bit hard
to use completely.
The stock vendor firmware is a downstream fork of U-Boot: <https://github.com/RaitoBezarius/uboot-nwa50ax>
with restricted boot commands. Fortunately, OpenWrt folks figured out trivial command injections,
so you can use most of the OpenWrt commands without trouble by just command injecting
atns, atna or atnf, e.g. atns "; $real_command".
From factory web UI, you can upload the result of the zyxel-nwa-fit output.
From another operating system, you need to `dumpimage -T flat_dt -p 0 $zyxel-nwa-fit -o firmware.bin`,
`flash_erase $(mtd partition of the target partition firmware or zy_firmware) 0 0`, then you complete by
`nandwrite -p $(mtd partition of the target partition firmware or zy_firmware) firmware.bin`.
How to put the firmware.bin on the machine is left to you as an exercise, e.g. SSH, TFTP, whatever.
From serial, you have two choices:
- Flash this system via U-Boot:
same reasoning as from an existing Linux system, two choices:
- ymodem the binary, perform the write manually, you can inspire yourself
from the `script` contained in the vendor firmware, those are just a FIT containing a script.
- prepare a FIT containing a script executing your commands, tftpboot this.
- boot from an existing Liminix system, e.g. TFTPBOOT image.
- boot from an OpenWrt system, i.e. follow OpenWrt steps.
Once you are in a Linux system, understand that this device has A/B boot.
OpenWrt provides you with `zyxel-bootconfig` to set/unset the image status and choice.
The kernel is booted with `bootImage=<number>` which tells you which slot are you on.
You should find yourself with 10ish MTD partitions, the most interesting ones are two:
- firmware: 40MB
- firmware_1: 40MB
In the current setup, they are split further into kernel (8MB) and ubi (32MB).
Once you are done with first installation, note that if you want to use the A/B feature,
you need to write a _secondary_ image on the slot B. There is no proper flashing code
that will set the being-updated slot to `new` and boot on it to verify if it's working.
This is a WIP.
Upgrading your system can be achieved via:
- `liminix-rebuild` for the userspace.
- `flash_erase` + `nandwrite` for the kernelspace to the other slot than the one you are booted on,
note that you can just nandwrite the mtd partition corresponding to the *kernel* and not the whole firmware.
If you soft-bricked your AP, i.e. you cannot boot anything in U-Boot, no worries, just plug the serial console,
prepare a TFTP server (via `tufted` for example), download vendor firmware, set up `atns`, `atnf`, etc. and run `atnz`.
This will reflash everything back to normal via TFTP.
If you hard-bricked your AP, i.e. U-Boot is telling you to transfer a valid bootloader via ymodem, just extract
a U-Boot from the vendor OS, send it via ymodem and use the previous operations to perform a full flash this time
of all partitions.
Note that if you erased your MRD partition, you lost your serial and MAC address. There's no way to recover the original one
except by reading the physical label on your device!
If you super-hard-bricked your AP, i.e. no output on serial console, congratulations, you reached one of the rare state
of this device. You need an external NAND flasher to repair it and write the first stage from Mediatek to continue the previous
recovery operations.
Development TODO list:
- Better support for upgrade automation w.r.t. to A/B, e.g. automagic scripts.
- Mount the logs partition, mount / as overlayfs of firmware ? rootfs and rootfs_data for extended data.
- Jitter-based entropy injection? Device can be slow to initialize its CRNG and hostapd will reject few clients at the start because of that.
- Defaults for hostapd based on MT7915 capabilities? See the example for one possible list.
- Remove primary/secondary hack and put it in preinit.
- Offer ways to reflash the *bootloader* itself to support direct boot via UBI and kernel upgrades via filesystem rewrite.
Vendor web page: https://www.zyxel.com/fr/fr/products/wireless/ax1800-wifi-6-dual-radio-nebulaflex-access-point-nwa50ax
OpenWrt web page: https://openwrt.org/inbox/toh/zyxel/nwa50ax
OpenWrt tech data: https://openwrt.org/toh/hwdata/zyxel/zyxel_nwa50ax
'';
module = { pkgs, config, lib, lim, ...}:
let
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) openwrt;
mac80211 = pkgs.mac80211.override {
drivers = [ "mt7915e" ];
klibBuild = config.system.outputs.kernel.modulesupport;
};
# v204520220929
wlan_firmware = pkgs.fetchurl {
url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_wa.bin";
hash = "sha256-wooyefzb0i8640+lwq3vNhcBXRFCtGuo+jiL7afZaKA=";
};
wlan_firmware' = pkgs.fetchurl {
url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_wm.bin";
hash = "sha256-k62nQewRuKjBLd5R3RxU4F74YKnQx5zr6gqMMImqVQw=";
};
wlan_firmware'' = pkgs.fetchurl {
url = "https://github.com/openwrt/mt76/raw/1b88dd07f153b202e57fe29734806744ed006b0e/firmware/mt7915_rom_patch.bin";
hash = "sha256-ifriAjWzFACrxVWCANZpUaEZgB/0pdbhnTVQytx6ddg=";
};
in {
imports = [
# We include it to ensure the bridge functionality
# is available on the target kernel.
../../modules/bridge
../../modules/arch/mipsel.nix
../../modules/outputs/tftpboot.nix
../../modules/outputs/zyxel-nwa-fit.nix
../../modules/zyxel-dual-image
];
filesystem = dir {
lib = dir {
firmware = dir {
mediatek = dir {
"mt7915_wa.bin" = symlink wlan_firmware;
"mt7915_wm.bin" = symlink wlan_firmware';
"mt7915_rom_patch.bin" = symlink wlan_firmware'';
};
};
};
};
rootfsType = "ubifs";
hardware = {
# Taken from OpenWRT
# root@OpenWrt:/# ubinfo /dev/ubi0
# ubi0
# Volumes count: 2
# Logical eraseblock size: 126976 bytes, 124.0 KiB
# Total amount of logical eraseblocks: 256 (32505856 bytes, 31.0 MiB)
# Amount of available logical eraseblocks: 0 (0 bytes)
# Maximum count of volumes 128
# Count of bad physical eraseblocks: 0
# Count of reserved physical eraseblocks: 19
# Current maximum erase counter value: 2
# Minimum input/output unit size: 2048 bytes
# Character device major/minor: 250:0
# Present volumes: 0, 1
ubi = {
minIOSize = "2048";
logicalEraseBlockSize = "126976";
physicalEraseBlockSize = "128KiB";
maxLEBcount = "256";
};
# This is a FIT containing a kernel padded and
# a UBI volume rootfs.
defaultOutput = "zyxel-nwa-fit";
loadAddress = lim.parseInt "0x80001000";
entryPoint = lim.parseInt "0x80001000";
# Aligned on 2kb.
alignment = 2048;
rootDevice = "ubi:rootfs";
dts = {
# Actually, this is not what we want.
# This DTS is insufficient.
src = ./mt7621_zyxel_nwa50ax.dtsi;
includes = [
# Here's one weird trick to make `ubi` detection
# out of the box.
# We will write ubi on /dev/firmware_a:rootfs location
# and same for /dev/firmware_b:rootfs.
# How do we distinguish both?
# We can just use the DTS to point ubi at A or B.
# This, unfortunately, means that we have "two images".
# But they are really just 1 image with 2 different DTS.
# TODO: improve this hack in preinit?
(if config.boot.imageType == "primary" then "${./a_image}" else "${./b_image}")
"${openwrt.src}/target/linux/ramips/dts"
];
};
networkInterfaces =
let
inherit (config.system.service.network) link;
in {
eth = link.build { ifname = "eth0"; };
lan = link.build { ifname = "lan"; };
wlan0 = link.build {
ifname = "wlan0";
dependencies = [ mac80211 ];
};
wlan1 = link.build {
ifname = "wlan1";
dependencies = [ mac80211 ];
};
};
};
boot = {
# Critical because NWA50AX will extend your cmdline with the image number booted.
# and some bootloader version.
# You don't want to find yourself being overridden.
commandLineDtbNode = "bootargs-override";
imageFormat = "fit";
tftp = {
# 5MB is nice.
freeSpaceBytes = 5 * 1024 * 1024;
loadAddress = lim.parseInt "0x2000000";
};
};
# Dual image management service in userspace.
services.zyxel-dual-image = config.boot.zyxel-dual-image.build {
ensureActiveImage = "primary";
# TODO: use mtd names rather…
# primary and secondary are always /dev/mtd3 by virtue of the
# dtb being not too wrong…
# TODO: remove this hack.
primaryMtdPartition = "/dev/mtd3";
secondaryMtdPartition = "/dev/mtd3";
bootConfigurationMtdPartition = "/dev/mtd12";
};
# DEVICE_VENDOR := ZyXEL
# KERNEL_SIZE := 8192k
# DEVICE_PACKAGES := kmod-mt7915-firmware zyxel-bootconfig
# KERNEL := kernel-bin | lzma | fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb
# IMAGES += factory.bin ramboot-factory.bin
# IMAGE/factory.bin := append-kernel | pad-to $$(KERNEL_SIZE) | append-ubi | zyxel-nwa-fit
# IMAGE/ramboot-factory.bin := append-kernel | pad-to $$(KERNEL_SIZE) | append-ubi
kernel = {
src = pkgs.fetchurl {
name = "linux.tar.gz";
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.137.tar.gz";
hash = "sha256-PkdzUKZ0IpBiWe/RS70J76JKnBFzRblWcKlaIFNxnHQ=";
};
extraPatchPhase = ''
${openwrt.applyPatches.ramips}
'';
config = {
RALINK = "y";
PCI = "y";
PHY_MT7621_PCI = "y";
PCIE_MT7621 = "y";
SOC_MT7621 = "y";
CLK_MT7621 = "y";
CLOCKSOURCE_WATCHDOG = "y";
SERIAL_8250_CONSOLE = "y";
SERIAL_8250 = "y";
SERIAL_CORE_CONSOLE = "y";
SERIAL_OF_PLATFORM = "y";
SERIAL_8250_NR_UARTS = "3";
SERIAL_8250_RUNTIME_UARTS = "3";
SERIAL_MCTRL_GPIO = "y";
CONSOLE_LOGLEVEL_DEFAULT = "8";
CONSOLE_LOGLEVEL_QUIET = "4";
# MTD_UBI_BEB_LIMIT = "20";
# MTD_UBI_WL_THRESHOLD = "4096";
MTD = "y";
MTD_BLOCK = "y"; # fix undefined ref to register_mtd_blktrans_dev
MTD_RAW_NAND = "y";
MTD_NAND_MT7621 = "y";
MTD_NAND_MTK_BMT = "y"; # Bad-block Management Table
MTD_NAND_ECC_SW_HAMMING= "y";
MTD_SPI_NAND= "y";
MTD_OF_PARTS = "y";
MTD_NAND_CORE= "y";
MTD_SPLIT_FIRMWARE= "y";
MTD_SPLIT_FIT_FW= "y";
PINCTRL = "y";
PINCTRL_MT7621 = "y";
I2C = "y";
I2C_MT7621 = "y";
SPI = "y";
MTD_SPI_NOR = "y";
SPI_MT7621 = "y";
SPI_MASTER = "y";
SPI_MEM = "y";
REGULATOR = "y";
REGULATOR_FIXED_VOLTAGE = "y";
RESET_CONTROLLER = "y";
POWER_RESET = "y";
POWER_RESET_GPIO = "y";
POWER_SUPPLY = "y";
LED_TRIGGER_PHY = "y";
PCI_DISABLE_COMMON_QUIRKS = "y";
PCI_DOMAINS = "y";
PCI_DOMAINS_GENERIC = "y";
PCI_DRIVERS_GENERIC = "y";
PCS_MTK_LYNXI = "y";
SOC_BUS = "y";
NET = "y";
ETHERNET = "y";
WLAN = "y";
PHYLIB = "y";
AT803X_PHY = "y";
FIXED_PHY = "y";
GENERIC_PHY = "y";
NET_DSA = "y";
NET_DSA_MT7530 = "y";
NET_DSA_MT7530_MDIO = "y";
NET_DSA_TAG_MTK = "y";
NET_MEDIATEK_SOC = "y";
NET_SWITCHDEV = "y";
NET_VENDOR_MEDIATEK = "y";
SWPHY = "y";
GPIOLIB = "y";
GPIO_MT7621 = "y";
OF_GPIO = "y";
EARLY_PRINTK = "y";
NEW_LEDS = "y";
LEDS_TRIGGERS = "y";
LEDS_CLASS = "y"; # required by rt2x00lib
LEDS_CLASS_MULTICOLOR = "y";
LEDS_BRIGHTNESS_HW_CHANGED = "y";
PRINTK_TIME = "y";
} // lib.optionalAttrs (config.system.service ? vlan) {
SWCONFIG = "y";
} // lib.optionalAttrs (config.system.service ? watchdog) {
RALINK_WDT = "y"; # watchdog
MT7621_WDT = "y"; # or it might be this one
};
};
};
}
@@ -1,56 +0,0 @@
#include "mt7621_zyxel_nwa-ax-for-ab.dtsi"
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/input/input.h>
/ {
compatible = "zyxel,nwa50ax", "mediatek,mt7621-soc";
model = "ZyXEL NWA50AX";
aliases {
led-boot = &led_system_green;
led-failsafe = &led_system_red;
led-running = &led_system_green;
led-upgrade = &led_system_red;
};
leds {
compatible = "gpio-leds";
led_system_red: system_red {
label = "red:system";
gpios = <&gpio 6 GPIO_ACTIVE_HIGH>;
};
led_system_green: system_green {
label = "green:system";
gpios = <&gpio 7 GPIO_ACTIVE_HIGH>;
};
system_blue {
label = "blue:system";
gpios = <&gpio 8 GPIO_ACTIVE_HIGH>;
};
};
keys {
compatible = "gpio-keys";
reset {
label = "reset";
gpios = <&gpio 30 GPIO_ACTIVE_LOW>;
linux,code = <KEY_RESTART>;
};
};
};
&ethernet {
pinctrl-0 = <&mdio_pins>, <&rgmii1_pins>;
};
&state_default {
gpio {
groups = "uart3", "rgmii2";
function = "gpio";
};
};
+7 -4
View File
@@ -1,9 +1,11 @@
{ eval, lib, pkgs }:
let
inherit (lib) types;
conf = eval.config;
rootDir = builtins.toPath ./..;
stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix) [ "${rootDir}/" ];
optToDoc = name: opt: {
stripAnyPrefixes = lib.flip (lib.fold lib.removePrefix)
["${rootDir}/"];
optToDoc = name: opt : {
inherit name;
description = opt.description or null;
default = opt.default or null;
@@ -24,6 +26,7 @@ let
let x = lib.mapAttrsToList optToDoc sd.parameters; in x;
}
else
item // { declarations = map stripAnyPrefixes item.declarations; };
item // { declarations = map stripAnyPrefixes item.declarations; };
in
builtins.map spliceServiceDefn (pkgs.lib.optionAttrSetToDocList eval.options)
builtins.map spliceServiceDefn
(pkgs.lib.optionAttrSetToDocList eval.options)
+18 -12
View File
@@ -1,18 +1,24 @@
with import <nixpkgs> { };
with import <nixpkgs> {} ;
let
inherit (builtins) stringLength readDir filter;
devices = filter (n: n != "families") (lib.mapAttrsToList (n: t: n) (readDir ../devices));
texts = map (
n:
let
d = import ../devices/${n}/default.nix;
d' = {
description = "${n}\n${substring 0 (stringLength n) "********************************"}\n";
} // d;
in
d'.description
) devices;
devices = filter (n: n != "families")
(lib.mapAttrsToList (n: t: n) (readDir ../devices));
texts = map (n:
let d = import ../devices/${n}/default.nix;
d' = {
description = "${n}\n${substring 0 (stringLength n) "********************************"}\n";
} // d;
installer =
if d ? description && d ? installer
then ''
The default installation route for this device is
:ref:`system-outputs-${d.installer}`
''
else "";
in d'.description)
devices;
in
writeText "hwdoc" ''
Supported hardware
+36 -28
View File
@@ -11,15 +11,15 @@
...
}: let
secrets = import ./extneder-secrets.nix;
inherit (pkgs.liminix.services) oneshot longrun target;
inherit (pkgs.liminix.services) oneshot longrun bundle target;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) writeText serviceFns;
inherit (pkgs) writeText dropbear ifwait serviceFns;
svc = config.system.service;
in rec {
boot = {
tftp = {
serverip = "10.0.0.1";
ipaddr = "10.0.0.8";
serverip = "192.168.8.148";
ipaddr = "192.168.8.251";
};
};
@@ -28,12 +28,34 @@ in rec {
../modules/network
../modules/vlan
../modules/ssh
../modules/usb.nix
../modules/watchdog
../modules/mount
];
hostname = "arhcive";
kernel = {
config = {
USB = "y";
USB_EHCI_HCD = "y";
USB_EHCI_HCD_PLATFORM = "y";
USB_OHCI_HCD = "y";
USB_OHCI_HCD_PLATFORM = "y";
USB_SUPPORT = "y";
USB_COMMON = "y";
USB_STORAGE = "y";
USB_STORAGE_DEBUG = "n";
USB_UAS = "y";
USB_ANNOUNCE_NEW_DEVICES = "y";
SCSI = "y";
BLK_DEV_SD = "y";
USB_PRINTER = "y";
MSDOS_PARTITION = "y";
EFI_PARTITION = "y";
EXT4_FS = "y";
EXT4_USE_FOR_EXT2 = "y";
FS_ENCRYPTION = "y";
};
};
services.dhcpc =
let iface = config.hardware.networkInterfaces.lan;
@@ -83,7 +105,7 @@ in rec {
};
services.mount_external_disk = svc.mount.build {
partlabel = "backup-disk";
device = "LABEL=backup-disk";
mountpoint = "/srv";
fstype = "ext4";
};
@@ -119,37 +141,23 @@ in rec {
secrets_file
services.mount_external_disk
config.hardware.networkInterfaces.lan
];
] ;
};
users.root = {
passwd = lib.mkForce secrets.root.passwd;
openssh.authorizedKeys.keys = secrets.root.keys;
# openssh.authorizedKeys.keys = [
# (builtins.readFile "/home/dan/.ssh/id_rsa.pub")
# ];
};
users.backup = {
uid = 500;
gid = 500;
gecos = "Storage owner";
dir = "/srv";
shell = "/dev/null";
uid=500; gid=500; gecos="Storage owner"; dir="/srv";
shell="/dev/null";
};
groups.backup = {
gid = 500;
usernames = [ "backup" ];
gid=500; usernames = ["backup"];
};
defaultProfile.packages = with pkgs; [
e2fsprogs
mtdutils
(levitate.override {
config = {
services = {
inherit (config.services) dhcpc sshd watchdog;
};
defaultProfile.packages = [ mtdutils ];
users.root.openssh.authorizedKeys.keys = secrets.root.keys;
};
})
];
defaultProfile.packages = with pkgs; [e2fsprogs strace tcpdump ];
}
+18 -23
View File
@@ -5,9 +5,9 @@
# wherever the text "EDIT" appears - please consult the tutorial
# documentation for details.
{ config, pkgs, ... }:
{ config, pkgs, lib, ... } :
let
inherit (pkgs.liminix.services) bundle oneshot;
inherit (pkgs.liminix.services) bundle oneshot longrun;
inherit (pkgs) serviceFns;
# EDIT: you can pick your preferred RFC1918 address space
# for NATted connections, if you don't like this one.
@@ -49,40 +49,31 @@ in rec {
country_code = "GB";
wpa_passphrase = "not a real wifi password";
hw_mode = "g";
hw_mode="g";
ieee80211n = 1;
auth_algs = 1; # 1=wpa2, 2=wep, 3=both
wpa = 2; # 1=wpa, 2=wpa2, 3=both
wpa = 2; # 1=wpa, 2=wpa2, 3=both
wpa_key_mgmt = "WPA-PSK";
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
rsn_pairwise = "CCMP"; # auth for wpa2
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
rsn_pairwise = "CCMP"; # auth for wpa2
wmm_enabled = 1;
};
};
services.int = svc.network.address.build {
interface = svc.bridge.primary.build { ifname = "int"; };
family = "inet";
address = "${ipv4LocalNet}.1";
prefixLength = 16;
family = "inet"; address = "${ipv4LocalNet}.1"; prefixLength = 16;
};
services.bridge = svc.bridge.members.build {
services.bridge = svc.bridge.members.build {
primary = services.int;
members = with config.hardware.networkInterfaces; [
wlan
lan
];
members = with config.hardware.networkInterfaces;
[ wlan lan ];
};
services.ntp = svc.ntp.build {
pools = {
"pool.ntp.org" = [ "iburst" ];
};
makestep = {
threshold = 1.0;
limit = 3;
};
pools = { "pool.ntp.org" = ["iburst"]; };
makestep = { threshold = 1.0; limit = 3; };
};
services.sshd = svc.ssh.build { };
@@ -166,7 +157,9 @@ in rec {
interface = services.wan;
};
services.firewall = svc.firewall.build { };
services.firewall = svc.firewall.build {
ruleset = import ./demo-firewall.nix;
};
services.packet_forwarding = svc.network.forward.build { };
@@ -203,5 +196,7 @@ in rec {
];
};
defaultProfile.packages = with pkgs; [ min-collect-garbage ];
defaultProfile.packages = with pkgs; [
min-collect-garbage
];
}
+101 -18
View File
@@ -8,10 +8,12 @@
config,
pkgs,
lib,
modulesPath,
...
}: let
secrets = import ./extneder-secrets.nix;
inherit (pkgs.liminix.services) oneshot longrun bundle target;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) dropbear ifwait serviceFns;
svc = config.system.service;
in rec {
boot = {
@@ -22,32 +24,113 @@ in rec {
};
imports = [
"${modulesPath}/profiles/wap.nix"
"${modulesPath}/vlan"
"${modulesPath}/ssh"
../modules/wlan.nix
../modules/vlan
../modules/network
../modules/hostapd
../modules/bridge
../modules/ssh
];
hostname = "extneder";
profile.wap = {
interfaces = with config.hardware.networkInterfaces; [
lan
wlan
];
kernel = {
config = {
wireless = {
networks.${secrets.ssid} = {
interface = config.hardware.networkInterfaces.wlan;
inherit (secrets) channel wpa_passphrase;
country_code = "GB";
hw_mode = "g";
wmm_enabled = 1;
ieee80211n = 1;
};
NETFILTER_XT_MATCH_CONNTRACK = "y";
IP6_NF_IPTABLES = "y"; # do we still need these
IP_NF_IPTABLES = "y"; # if using nftables directly
# these are copied from rotuer and need review.
# we're not running a firewall, so why do we need
# nftables config?
IP_NF_NAT = "y";
IP_NF_TARGET_MASQUERADE = "y";
NETFILTER = "y";
NETFILTER_ADVANCED = "y";
NETFILTER_XTABLES = "y";
NFT_COMPAT = "y";
NFT_CT = "y";
NFT_LOG = "y";
NFT_MASQ = "y";
NFT_NAT = "y";
NFT_REJECT = "y";
NFT_REJECT_INET = "y";
NF_CONNTRACK = "y";
NF_NAT = "y";
NF_NAT_MASQUERADE = "y";
NF_TABLES = "y";
NF_TABLES_INET = "y";
NF_TABLES_IPV4 = "y";
NF_TABLES_IPV6 = "y";
};
};
services.hostap = svc.hostapd.build {
interface = config.hardware.networkInterfaces.wlan;
params = {
country_code = "GB";
hw_mode = "g";
wmm_enabled = 1;
ieee80211n = 1;
inherit (secrets) ssid channel wpa_passphrase;
auth_algs = 1; # 1=wpa2, 2=wep, 3=both
wpa = 2; # 1=wpa, 2=wpa2, 3=both
wpa_key_mgmt = "WPA-PSK";
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
rsn_pairwise = "CCMP"; # auth for wpa2
};
};
services.int = svc.bridge.primary.build {
ifname = "int";
};
services.dhcpc = svc.network.dhcp.client.build {
interface = services.int;
dependencies = [ config.services.hostname ];
};
services.bridge = svc.bridge.members.build {
primary = services.int;
members = with config.hardware.networkInterfaces; [
lan
wlan
];
};
services.sshd = svc.ssh.build {};
services.resolvconf = oneshot rec {
dependencies = [ services.dhcpc ];
name = "resolvconf";
# CHECK: https://udhcp.busybox.net/README.udhcpc says
# 'A list of DNS server' but doesn't say what separates the
# list members. Assuming it's a space or other IFS character
up = ''
. ${serviceFns}
( in_outputs ${name}
for i in $(output ${services.dhcpc} dns); do
echo "nameserver $i" > resolv.conf
done
)
'';
};
filesystem = dir {
etc = dir {
"resolv.conf" = symlink "${services.resolvconf}/.outputs/resolv.conf";
};
};
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.dhcpc} router)";
target = "default";
dependencies = [services.dhcpc];
};
users.root.passwd = lib.mkForce secrets.root.passwd;
defaultProfile.packages = with pkgs; [nftables strace tcpdump swconfig];
}
+2 -1
View File
@@ -1,5 +1,6 @@
{ config, pkgs, ... } :
{ config, pkgs, lib, ... } :
let
inherit (pkgs) serviceFns;
svc = config.system.service;
in rec {
+2 -1
View File
@@ -1,5 +1,6 @@
{ config, pkgs, ... } :
{ config, pkgs, lib, ... } :
let
inherit (pkgs) serviceFns;
svc = config.system.service;
in rec {
-177
View File
@@ -1,177 +0,0 @@
{
config,
pkgs,
lib,
...
}: let
secrets = import ./extneder-secrets.nix;
rsecrets = import ./rotuer-secrets.nix;
# https://support.aa.net.uk/Category:Incoming_L2TP says:
# "Please use the DNS name (l2tp.aa.net.uk) instead of hardcoding an
# IP address; IP addresses can and do change. If you have to use an
# IP, use 194.4.172.12, but do check the DNS for l2tp.aa.net.uk in
# case it changes."
# but (1) we don't want to use the wwan stick's dns as our main
# resolver: it's provided by some mobile ISP and they aren't
# necessarily the best at providing unfettered services without
# deciding to do something weird; (2) it's not simple to arrange
# that xl2tpd gets a different resolver than every other process;
# (3) there's no way to specify an lns address to xl2tpd at runtime
# except by rewriting its config file. So what we will do is lookup
# the lns hostname using the mobile ISP's dns server and then refuse
# to start l2tp unless the expected lns address is one of the
# addresses returned. I think this satisfies "do check the DNS"
lns = { hostname = "l2tp.aaisp.net.uk"; address = "194.4.172.12"; };
inherit (pkgs.liminix.services) oneshot longrun target;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) serviceFns;
svc = config.system.service;
in rec {
boot = {
tftp = {
serverip = "10.0.0.1";
ipaddr = "10.0.0.8";
};
};
imports = [
../modules/cdc-ncm
../modules/network
../modules/vlan
../modules/ssh
../modules/usb.nix
../modules/watchdog
../modules/mount
../modules/ppp
];
hostname = "thing";
services.wwan = svc.wwan.build {
apn = "data.uk";
username = "user";
password = "one2one";
authType = "chap";
};
services.wan =
let
z = final : prev: {
controller = longrun rec {
name = "wan-switcher";
run = ''
(in_outputs ${name}
${pkgs.s6-rc-round-robin}/bin/s6-rc-round-robin \
-p ${final.proxy.name} \
${lib.concatStringsSep " "
(builtins.map (f: f.name) [final.pppoe final.l2tp])}
)
'';
};
pppoe = (svc.pppoe.build {
interface = config.hardware.networkInterfaces.wan;
ppp-options = [
"debug" "+ipv6" "noauth"
"name" rsecrets.l2tp.name
"password" rsecrets.l2tp.password
];
}).overrideAttrs(o: { inherit (final) controller; });
l2tp =
let
check-address = oneshot rec {
name = "check-lns-address";
up = "grep -Fx ${ lns.address} $(output_path ${services.lns-address} addresses)";
dependencies = [ services.lns-address ];
};
route = svc.network.route.build {
via = "$(output ${services.dhcpc} router)";
target = lns.address;
dependencies = [services.dhcpc check-address];
};
in (svc.l2tp.build {
lns = lns.address;
ppp-options = [
"debug" "+ipv6" "noauth"
"name" rsecrets.l2tp.name
"connect-delay" "5000"
"password" rsecrets.l2tp.password
];
dependencies = [config.services.lns-address route check-address];
}).overrideAttrs(o: { inherit (final) controller; });
proxy = oneshot rec {
name = "wan-proxy";
inherit (final) controller;
buildInputs = with final; [ pppoe l2tp];
up = ''
echo start proxy ${name}
set -x
(in_outputs ${name}
cp -rv $(output_path ${final.controller} active)/* .
)
'';
};
};
in (lib.fix (lib.extends z (prev : { }))).proxy;
services.sshd = svc.ssh.build { };
services.resolvconf = oneshot rec {
dependencies = [ services.wan ];
name = "resolvconf";
up = ''
. ${serviceFns}
( in_outputs ${name}
for i in ns1 ns2 ; do
ns=$(output ${services.wan} $i)
echo "nameserver $ns" >> resolv.conf
done
)
'';
};
filesystem = dir {
etc = dir {
"resolv.conf" = symlink "${services.resolvconf}/.outputs/resolv.conf";
};
};
services.dhcpc = svc.network.dhcp.client.build {
interface = config.services.wwan;
dependencies = [ config.services.hostname ];
};
services.lns-address = let
ns = "$(output_word ${services.dhcpc} dns 1)";
route-to-bootstrap-nameserver = svc.network.route.build {
via = "$(output ${services.dhcpc} router)";
target = ns;
dependencies = [services.dhcpc];
};
in oneshot rec {
name = "resolve-l2tp-server";
dependencies = [ services.dhcpc route-to-bootstrap-nameserver ];
up = ''
(in_outputs ${name}
DNSCACHEIP="${ns}" ${pkgs.s6-dns}/bin/s6-dnsip4 ${lns.hostname} \
> addresses
)
'';
};
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.wan} peer-address)";
target = "default";
dependencies = [services.wan];
};
# defaultProfile.packages = [ pkgs.go-l2tp ];
users.root = {
passwd = lib.mkForce secrets.root.passwd;
openssh.authorizedKeys.keys = secrets.root.keys;
};
}
-119
View File
@@ -1,119 +0,0 @@
{ config, pkgs, ... } :
let
inherit (pkgs.liminix.services) target;
svc = config.system.service;
secrets-1 = {
ssid = "Zyxel 2G (N)";
wpa_passphrase = "diamond dogs";
};
secrets-2 = {
ssid = "Zyxel 5G (AX)";
wpa_passphrase = "diamond dogs";
};
baseParams = {
country_code = "FR";
hw_mode = "g";
channel = 6;
wmm_enabled = 1;
ieee80211n = 1;
ht_capab = "[LDPC][GF][HT40-][HT40+][SHORT-GI-40][MAX-AMSDU-7935][TX-STBC]";
auth_algs = 1;
wpa = 2;
wpa_key_mgmt = "WPA-PSK";
wpa_pairwise = "TKIP CCMP";
rsn_pairwise = "CCMP";
};
modernParams = {
hw_mode = "a";
he_su_beamformer = 1;
he_su_beamformee = 1;
he_mu_beamformer = 1;
preamble = 1;
# Allow radar detection.
ieee80211d = 1;
ieee80211h = 1;
ieee80211ac = 1;
ieee80211ax = 1;
vht_capab = "[MAX-MPDU-7991][SU-BEAMFORMEE][SU-BEAMFORMER][RXLDPC][SHORT-GI-80][MAX-A-MPDU-LEN-EXP3][RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN][TX-STBC-2BY1][RX-STBC-1][MU-BEAMFORMER]";
vht_oper_chwidth = 1;
he_oper_chwidth = 1;
channel = 36;
vht_oper_centr_freq_seg0_idx = 42;
he_oper_centr_freq_seg0_idx = 42;
require_vht = 1;
};
mkWifiSta = params: interface: secrets: svc.hostapd.build {
inherit interface;
params = params // {
inherit (secrets) ssid wpa_passphrase;
};
};
in rec {
imports = [
../modules/wlan.nix
../modules/network
../modules/hostapd
../modules/ssh
../modules/ntp
../modules/vlan
../modules/bridge
];
hostname = "zyxel";
users.root = {
# EDIT: choose a root password and then use
# "mkpasswd -m sha512crypt" to determine the hash.
# It should start wirh $6$.
passwd = "$y$j9T$f8GhLiqYmr3lc58eKhgyD0$z7P/7S9u.kq/cANZExxhS98bze/6i7aBxU6tbl7RMi.";
openssh.authorizedKeys.keys = [
# EDIT: you can add your ssh pubkey here
# "ssh-rsa AAAAB3NzaC1....H6hKd user@example.com";
];
};
services.int = svc.bridge.primary.build {
ifname = "int";
};
services.bridge = svc.bridge.members.build {
primary = services.int;
members = with config.hardware.networkInterfaces; [
lan
wlan0
wlan1
];
};
services.dhcpv4 =
let iface = services.int;
in svc.network.dhcp.client.build { interface = iface; };
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.dhcpv4} address)";
target = "default";
dependencies = [ services.dhcpv4 ];
};
services.packet_forwarding = svc.network.forward.build { };
services.sshd = svc.ssh.build {
allowRoot = true;
};
services.ntp = config.system.service.ntp.build {
pools = { "pool.ntp.org" = ["iburst"] ; };
};
boot.tftp = {
serverip = "192.0.2.10";
ipaddr = "192.0.2.12";
};
# wlan0 is the 2.4GHz interface.
services.hostap-1 = mkWifiSta baseParams config.hardware.networkInterfaces.wlan0 secrets-1;
# wlan1 is the 5GHz interface, e.g. AX capable.
services.hostap-2 = mkWifiSta (baseParams // modernParams) config.hardware.networkInterfaces.wlan1 secrets-2;
defaultProfile.packages = with pkgs; [ zyxel-bootconfig iw min-collect-garbage mtdutils ];
}
+3 -3
View File
@@ -3,8 +3,8 @@ let
inherit (pkgs) serviceFns;
svc = config.system.service;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs.liminix.services) oneshot target;
some-util-linux = pkgs.runCommand "some-util-linux" { } ''
inherit (pkgs.liminix.services) oneshot longrun bundle target;
some-util-linux = pkgs.runCommand "some-util-linux" {} ''
mkdir -p $out/bin
cd ${pkgs.util-linux-small}/bin
cp fdisk sfdisk mkswap $out/bin
@@ -53,7 +53,7 @@ in rec {
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.dhcpc} router)";
target = "default";
dependencies = [ services.dhcpc ];
dependencies = [services.dhcpc];
};
services.resolvconf = oneshot rec {
+3 -1
View File
@@ -8,10 +8,12 @@
root = {
# mkpasswd -m sha512crypt
passwd = "$6$6pt0mpbgcB7kC2RJ$kSBoCYGyi1.qxt7dqmexLj1l8E6oTZJZmfGyJSsMYMW.jlsETxdgQSdv6ptOYDM7DHAwf6vLG0pz3UD31XBfC1";
openssh.authorizedKeys.keys = [ ];
openssh.authorizedKeys.keys = [
];
};
lan = {
prefix = "10.8.0";
};
}
+155 -62
View File
@@ -6,16 +6,23 @@
# problems.
{ config, pkgs, lib, modulesPath, ... } :
{ config, pkgs, lib, ... } :
let
secrets = {
domainName = "fake.liminix.org";
firewallRules = { };
firewallRules = {};
} // (import ./rotuer-secrets.nix);
inherit (pkgs.liminix.services) oneshot longrun bundle;
inherit (pkgs) serviceFns;
svc = config.system.service;
wirelessConfig = {
wirelessConfig = {
country_code = "GB";
inherit (secrets) wpa_passphrase;
auth_algs = 1; # 1=wpa2, 2=wep, 3=both
wpa = 2; # 1=wpa, 2=wpa2, 3=both
wpa_key_mgmt = "WPA-PSK";
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
rsn_pairwise = "CCMP"; # auth for wpa2
wmm_enabled = 1;
};
@@ -29,62 +36,65 @@ in rec {
};
imports = [
"${modulesPath}/profiles/gateway.nix"
"${modulesPath}/schnapps"
"${modulesPath}/outputs/btrfs.nix"
"${modulesPath}/outputs/extlinux.nix"
../modules/wlan.nix
../modules/network
../modules/ppp
../modules/dnsmasq
../modules/dhcp6c
../modules/firewall
../modules/hostapd
../modules/bridge
../modules/ntp
../modules/schnapps
../modules/ssh
../modules/outputs/btrfs.nix
../modules/outputs/extlinux.nix
];
hostname = "rotuer";
rootfsType = "btrfs";
rootOptions = "subvol=@";
boot.loader.extlinux.enable = true;
profile.gateway = {
lan = {
interfaces = with config.hardware.networkInterfaces;
[
wlan wlan5
lan0 lan1 lan2 lan3 lan4
];
inherit (secrets.lan) prefix;
address = {
family = "inet"; address ="${secrets.lan.prefix}.1"; prefixLength = 24;
};
dhcp = {
start = 10;
end = 240;
hosts = { } // lib.optionalAttrs (builtins.pathExists ./static-leases.nix) (import ./static-leases.nix);
localDomain = "lan";
};
};
wan = {
interface = config.hardware.networkInterfaces.wan;
username = secrets.l2tp.name;
password = secrets.l2tp.password;
dhcp6.enable = true;
};
firewall = {
enable = true;
rules = secrets.firewallRules;
};
wireless.networks = {
"${secrets.ssid}" = {
interface = config.hardware.networkInterfaces.wlan;
hw_mode = "g";
channel = "2";
ieee80211n = 1;
} // wirelessConfig;
"${secrets.ssid}5" = rec {
interface = config.hardware.networkInterfaces.wlan5;
hw_mode = "a";
channel = 36;
ht_capab = "[HT40+]";
vht_oper_chwidth = 1;
vht_oper_centr_freq_seg0_idx = channel + 6;
ieee80211n = 1;
ieee80211ac = 1;
} // wirelessConfig;
};
services.hostap = svc.hostapd.build {
interface = config.hardware.networkInterfaces.wlan;
params = {
ssid = secrets.ssid;
hw_mode="g";
channel = "2";
ieee80211n = 1;
} // wirelessConfig;
};
services.hostap5 = svc.hostapd.build {
interface = config.hardware.networkInterfaces.wlan5;
params = rec {
ssid = "${secrets.ssid}5";
hw_mode="a";
channel = 36;
ht_capab = "[HT40+]";
vht_oper_chwidth = 1;
vht_oper_centr_freq_seg0_idx = channel + 6;
ieee80211n = 1;
ieee80211ac = 1;
} // wirelessConfig;
};
services.int = svc.network.address.build {
interface = svc.bridge.primary.build { ifname = "int"; };
family = "inet"; address ="${secrets.lan.prefix}.1"; prefixLength = 24;
};
services.bridge = svc.bridge.members.build {
primary = services.int;
members = with config.hardware.networkInterfaces;
[ wlan
wlan5
lan0
lan1
lan2
lan3
lan4
];
};
services.ntp = svc.ntp.build {
@@ -96,20 +106,103 @@ in rec {
users.root = secrets.root;
services.dns =
let interface = services.int;
in svc.dnsmasq.build {
resolvconf = services.resolvconf;
inherit interface;
ranges = [
"${secrets.lan.prefix}.10,${secrets.lan.prefix}.240"
# ra-stateless: sends router advertisements with the O and A
# bits set, and provides a stateless DHCP service. The client
# will use a SLAAC address, and use DHCP for other
# configuration information.
"::,constructor:$(output ${interface} ifname),ra-stateless"
];
# You can add static addresses for the DHCP server here. I'm
# not putting my actual MAC addresses in a public git repo ...
hosts = { } // lib.optionalAttrs (builtins.pathExists ./static-leases.nix) (import ./static-leases.nix);
upstreams = [ "/${secrets.domainName}/" ];
domain = secrets.domainName;
};
services.wan = svc.pppoe.build {
interface = config.hardware.networkInterfaces.wan;
ppp-options = [
"debug" "+ipv6" "noauth"
"name" secrets.l2tp.name
"password" secrets.l2tp.password
];
};
services.resolvconf = oneshot rec {
dependencies = [ services.wan ];
name = "resolvconf";
up = ''
. ${serviceFns}
( in_outputs ${name}
echo "nameserver $(output ${services.wan} ns1)" > resolv.conf
echo "nameserver $(output ${services.wan} ns2)" >> resolv.conf
chmod 0444 resolv.conf
)
'';
};
filesystem =
let inherit (pkgs.pseudofile) dir symlink;
in dir {
etc = dir {
"resolv.conf" = symlink "${services.resolvconf}/.outputs/resolv.conf";
};
};
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.wan} address)";
target = "default";
dependencies = [ services.wan ];
};
services.defaultroute6 = svc.network.route.build {
via = "$(output ${services.wan} ipv6-peer-address)";
target = "default";
interface = services.wan;
};
services.firewall = svc.firewall.build {
ruleset =
let defaults = import ./demo-firewall.nix;
in lib.recursiveUpdate defaults secrets.firewallRules;
};
services.packet_forwarding = svc.network.forward.build { };
services.dhcp6c =
let client = svc.dhcp6c.client.build {
interface = services.wan;
};
in bundle {
name = "dhcp6c";
contents = [
(svc.dhcp6c.prefix.build {
inherit client;
interface = services.int;
})
(svc.dhcp6c.address.build {
inherit client;
interface = services.wan;
})
];
};
defaultProfile.packages = with pkgs; [
min-collect-garbage
nftables
strace
tcpdump
s6
];
programs.busybox = {
applets = [
"fdisk" "sfdisk"
];
options = {
FEATURE_FANCY_TAIL = "y";
};
};
programs.busybox.applets = [
"fdisk" "sfdisk"
];
}
+2 -1
View File
@@ -1,5 +1,6 @@
{ config, pkgs, lim, ... } :
{ config, pkgs, lib, lim, ... } :
let
inherit (pkgs) serviceFns;
svc = config.system.service;
in rec {
+8 -9
View File
@@ -9,29 +9,28 @@
./busybox.nix
./dhcp6c
./dnsmasq
./outputs/ext4fs.nix
./firewall
./hardware.nix
./hostapd
./hostname.nix
./outputs/initramfs.nix
./outputs/jffs2.nix
./kernel
./mdevd.nix
./outputs/kexecboot.nix
./mount
./network
./ntp
./outputs.nix
./outputs/ext4fs.nix
./outputs/initramfs.nix
./outputs/jffs2.nix
./outputs/kexecboot.nix
./outputs/mtdimage.nix
./outputs/tftpboot.nix
./outputs/ubifs.nix
./outputs/ubimage.nix
./outputs/vmroot.nix
./outputs/ubimage.nix
./outputs/mtdimage.nix
./ppp
./ramdisk.nix
./squashfs.nix
./ssh
./outputs/tftpboot.nix
./outputs/ubifs.nix
./users.nix
./vlan
./watchdog
+1 -1
View File
@@ -1,4 +1,4 @@
{ lim, pkgs, config, ...}:
{ lib, lim, pkgs, config, ...}:
{
config = {
kernel.config = {
+1 -1
View File
@@ -1,4 +1,4 @@
{ lim, pkgs, config, ...}:
{ lib, lim, pkgs, config, ...}:
{
config = {
kernel.config = {
+1 -1
View File
@@ -1,4 +1,4 @@
{ config, lim, ...}:
{ lib, pkgs, config, lim, ...}:
{
config = {
kernel.config = {
+1 -1
View File
@@ -1,4 +1,4 @@
{ pkgs, config, ...}:
{ lib, pkgs, config, ...}:
{
imports = [ ./mips.nix ];
config = {
+1 -1
View File
@@ -1,4 +1,4 @@
{ config, ...}:
{ lib, pkgs, config, ...}:
{
imports = [ ./mips.nix ];
config = {
+11 -51
View File
@@ -4,12 +4,17 @@
{ lib, pkgs, config, ...}:
let
inherit (lib) mkOption types;
inherit (lib) mkEnableOption mkOption types isDerivation hasAttr ;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs.liminix.networking) address interface;
inherit (pkgs.liminix.services) bundle;
type_service = pkgs.liminix.lib.types.service;
in {
imports = [
./kernel # kernel is a separate module for doc purposes
];
options = {
defaultProfile = {
packages = mkOption {
@@ -24,10 +29,6 @@ in {
services = mkOption {
type = types.attrsOf type_service;
};
system.callService = mkOption {
type = types.functionTo (types.functionTo types.anything);
};
filesystem = mkOption {
type = types.anything;
description = ''
@@ -36,7 +37,7 @@ in {
'';
# internal = true; # probably a good case to make this internal
};
rootfsType = mkOption {
rootfsType = mkOption {
default = "squashfs";
type = types.enum [
"btrfs"
@@ -46,7 +47,7 @@ in {
"ubifs"
];
};
rootOptions = mkOption {
rootOptions = mkOption {
type = types.nullOr types.str;
default = null;
};
@@ -54,29 +55,11 @@ in {
boot = {
commandLine = mkOption {
type = types.listOf types.nonEmptyStr;
default = [ ];
default = [];
description = "Kernel command line";
};
commandLineDtbNode = mkOption {
type = types.enum [
"bootargs"
"bootargs-override"
];
default = "bootargs";
description = "Kernel command line's devicetree node";
};
imageType = mkOption {
type = types.enum [
"primary"
"secondary"
];
default = "primary";
};
imageFormat = mkOption {
type = types.enum [
"fit"
"uimage"
];
type = types.enum ["fit" "uimage"];
default = "uimage";
};
tftp = {
@@ -92,7 +75,7 @@ in {
};
# These names match the uboot environment variables. I reserve
# the right to change them if I think of better ones.
ipaddr = mkOption {
ipaddr = mkOption {
type = types.str;
description = ''
Our IP address to use when creating scripts to
@@ -119,29 +102,6 @@ in {
"fw_devlink=off"
] ++ lib.optional (config.rootOptions != null) "rootflags=${config.rootOptions}";
system.callService = path : parameters :
let
typeChecked = caller: type: value:
let
inherit (lib) types mergeDefinitions;
defs = [{ file = caller; inherit value; }];
type' = types.submodule { options = type; };
in (mergeDefinitions [] type' defs).mergedValue;
cp = lib.callPackageWith(pkgs // { svc = config.system.service; });
pkg = cp path {};
checkTypes = t : p : typeChecked (builtins.toString path) t p;
in {
inherit parameters;
build = { dependencies ? [], ... } @ args :
let
s = pkg (checkTypes parameters
(builtins.removeAttrs args ["dependencies"]));
in s.overrideAttrs (o: {
dependencies = dependencies ++ o.dependencies;
buildInputs = dependencies ++ o.buildInputs;
});
};
users.root = {
uid = 0; gid= 0; gecos = "Root of all evaluation";
dir = "/home/root/";
+3 -4
View File
@@ -10,11 +10,10 @@
{ lib, pkgs, config, ...}:
let
inherit (lib) mkOption types;
inherit (pkgs.liminix.services) oneshot;
inherit (pkgs) liminix;
in
{
imports = [ ../ifwait ];
options = {
system.service.bridge = {
primary = mkOption { type = liminix.lib.types.serviceDefn; };
@@ -28,7 +27,7 @@ in
description = "bridge interface name to create";
};
};
members = config.system.callService ./members.nix {
members = liminix.callService ./members.nix {
primary = mkOption {
type = liminix.lib.types.interface;
description = "primary bridge interface";
@@ -48,5 +47,5 @@ in
# a better way to test for the existence of vlan config:
# maybe the module should set an `enabled` attribute?
BRIDGE_VLAN_FILTERING = "y";
};
};
}
+9 -14
View File
@@ -1,28 +1,23 @@
{
liminix
, ifwait
, svc
, lib
}:
{ members, primary } :
let
inherit (liminix.networking) interface;
inherit (liminix.services) bundle oneshot;
inherit (lib) mkOption types;
addif = member :
# how do we get sight of services from here? maybe we need to
# implement ifwait as a regualr derivation instead of a
# servicedefinition
svc.ifwait.build {
state = "running";
interface = member;
oneshot {
name = "${primary.name}.member.${member.name}";
up = ''
dev=$(output ${member} ifname)
${ifwait}/bin/ifwait $dev running && ip link set dev $dev master $(output ${primary} ifname)
'';
down = "ip link set dev $(output ${member} ifname) nomaster";
dependencies = [ primary member ];
service = oneshot {
name = "${primary.name}.member.${member.name}";
up = ''
ip link set dev $(output ${member} ifname) master $(output ${primary} ifname)
'';
down = "ip link set dev $(output ${member} ifname) nomaster";
};
};
in bundle {
name = "${primary.name}.members";
+3 -1
View File
@@ -1,10 +1,12 @@
{
liminix
, ifwait
, lib
}:
{ ifname } :
let
inherit (liminix.services) oneshot;
inherit (liminix.services) bundle oneshot;
inherit (lib) mkOption types;
in oneshot rec {
name = "${ifname}.link";
up = ''
+22 -23
View File
@@ -8,7 +8,7 @@
{ lib, pkgs, config, ...}:
let
inherit (lib) mkOption types mapAttrsToList;
inherit (lib) mkOption mkEnableOption types mapAttrsToList;
inherit (pkgs.pseudofile) dir symlink;
inherit (lib.strings) toUpper;
@@ -32,21 +32,23 @@ let
(a: symlink "${busybox}/bin/busybox");
minimalApplets = [
# this is probably less minimal than it could be
"arch" "ash" "base64" "basename" "bc" "brctl" "bunzip2" "bzcat" "bzip2"
"cal" "cat" "chattr" "chgrp" "chmod" "chown" "chpst" "chroot" "clear" "cmp"
"comm" "cp" "cpio" "cut" "date" "dhcprelay" "dd" "df" "dirname" "dmesg"
"du" "echo" "egrep" "env" "expand" "expr" "false" "fdisk" "fgrep" "find"
"free" "fuser" "grep" "gunzip" "gzip" "head" "hexdump" "hostname" "hwclock"
"ifconfig" "ip" "ipaddr" "iplink" "ipneigh" "iproute" "iprule" "kill"
"killall" "killall5" "less" "ln" "ls" "lsattr" "lsof" "md5sum" "mkdir"
"mknod" "mktemp" "mount" "mv" "nc" "netstat" "nohup" "od" "pgrep" "pidof"
"ping" "ping6" "pkill" "pmap" "printenv" "printf" "ps" "pwd" "readlink"
"realpath" "reset" "rm" "rmdir" "route" "sed" "seq" "setsid" "sha1sum"
"sha256sum" "sha512sum" "sleep" "sort" "stat" "strings" "stty" "su" "sum"
"swapoff" "swapon" "sync" "tail" "tee" "test" "time" "touch" "tr"
"traceroute" "traceroute6" "true" "truncate" "tty" "udhcpc" "umount"
"uname" "unexpand" "uniq" "unlink" "unlzma" "unxz" "unzip" "uptime" "watch"
"wc" "whoami" "xargs" "xxd" "xz" "xzcat" "yes" "zcat"
"arch" "ash" "base64" "basename" "bc" "brctl" "bunzip2" "bzcat"
"bzip2" "cal" "cat" "chattr" "chgrp" "chmod" "chown" "chpst"
"chroot" "clear" "cmp" "comm" "cp" "cpio" "cut" "date" "dd" "df"
"dirname" "dmesg" "du" "echo" "egrep" "env" "expand" "expr"
"false" "fdisk" "fgrep" "find" "free" "fuser" "grep" "gunzip"
"gzip" "head" "hexdump" "hostname" "hwclock" "ifconfig" "ip"
"ipaddr" "iplink" "ipneigh" "iproute" "iprule" "kill" "killall"
"killall5" "less" "ln" "ls" "lsattr" "lsof" "md5sum" "mkdir"
"mknod" "mktemp" "mount" "mv" "nc" "netstat" "nohup" "od" "pgrep"
"pidof" "ping" "ping6" "pkill" "pmap" "printenv" "printf" "ps"
"pwd" "readlink" "realpath" "reset" "rm" "rmdir" "route" "sed"
"seq" "setsid" "sha1sum" "sha256sum" "sha512sum" "sleep" "sort"
"stat" "strings" "stty" "su" "sum" "swapoff" "swapon" "sync"
"tail" "tee" "test" "time" "touch" "tr" "traceroute" "traceroute6"
"true" "truncate" "tty" "udhcpc" "umount" "uname"
"unexpand" "uniq" "unlink" "unlzma" "unxz" "unzip" "uptime"
"watch" "wc" "whoami" "xargs" "xxd" "xz" "xzcat" "yes" "zcat"
];
in {
options = {
@@ -85,13 +87,10 @@ in {
};
};
filesystem = dir {
bin = dir (
{
busybox = symlink "${busybox}/bin/busybox";
sh = symlink "${busybox}/bin/busybox";
}
// makeLinks
);
bin = dir ({
busybox = symlink "${busybox}/bin/busybox";
sh = symlink "${busybox}/bin/busybox";
} // makeLinks);
};
};
}
-31
View File
@@ -1,31 +0,0 @@
{ config, pkgs, lib, ... }:
let
inherit (pkgs) liminix;
inherit (lib) mkOption types;
in {
imports = [
../service-trigger
];
options = {
system.service.wwan = mkOption {
type = liminix.lib.types.serviceDefn;
};
};
config = {
kernel.config = {
USB_NET_HUAWEI_CDC_NCM = "y";
USB_USBNET = "y";
USB_SERIAL = "y";
USB_SERIAL_OPTION = "y";
};
# https://www.0xf8.org/2017/01/flashing-a-huawei-e3372h-4g-lte-stick-from-hilink-to-stick-mode/
system.service.wwan = config.system.callService ./wwan.nix {
apn = mkOption { type = types.str; };
username = mkOption { type = types.str; };
password = mkOption { type = types.str; };
authType = mkOption { type = types.enum [ "pap" "chap" ]; };
};
};
}
-67
View File
@@ -1,67 +0,0 @@
{
liminix
, usb-modeswitch
, ppp
, lib
, svc
, uevent-watch
}:
{ apn, username, password, authType }:
let
inherit (liminix.services) oneshot;
authTypeNum = if authType == "pap" then "1" else "2";
chat = lib.escapeShellArgs [
# Your usb modem thing might present as a tty that you run PPP
# over, or as a network device ("ndis" or "ncm"). The latter
# kind is to be preferred, at least in principle, because it's
# faster. This initialization sequence works for the Huawei
# E3372, and took much swearing: the error messages are *awful*
"" "AT"
"OK" "ATZ"
# create PDP context
"OK" "AT+CGDCONT=1,\"IP\",\"${apn}\""
# activate PDP context
"OK" "AT+CGACT=1,1"
# setup username and password per requirements of sim provider.
# (caret is special to chat, so needs escaping in AT commands)
"OK" "AT\\^AUTHDATA=1,${authTypeNum},\"\",\"${password}\",\"${username}\""
# start the thing (I am choosing to read this as "NDIS DialUP")
"OK" "AT\\^NDISDUP=1,1"
"OK"
];
modeswitch = oneshot rec {
name = "modem-modeswitch";
controller = (svc.uevent-rule.build {
serviceName = name;
terms = { devtype = "usb_device"; product = "12d1/14fe/102"; };
});
up = ''
${usb-modeswitch}/bin/usb_modeswitch -v 12d1 -p 14fe --huawei-new-mode
'';
};
atz = oneshot rec {
name = "modem-atz";
dependencies = [ modeswitch ];
controller = (svc.uevent-rule.build {
serviceName = name;
terms = {
subsystem = "tty";
attrs = {
idVendor = "12d1";
idProduct = "1506";
};
};
symlink = "/dev/modem";
});
up = ''
ls -l /dev/modem
test -L /dev/modem || exit 1
${ppp}/bin/chat -s -v ${chat} 0<>/dev/modem 1>&0
'';
down = "${ppp}/bin/chat -v '' ATZ OK 0<>/dev/modem 1>&0";
};
in svc.network.link.build {
ifname = "wwan0";
dependencies = [ atz ];
}
+27 -19
View File
@@ -1,32 +1,40 @@
(local { : system } (require :anoia))
(local svc (require :anoia.svc))
(fn deletions [old-addresses new-addresses]
(let [deleted {}]
(fn changes [old-addresses new-addresses]
(let [added {}
deleted {}]
(each [n address (pairs new-addresses)]
(if (not (. old-addresses n))
(table.insert added address)))
(each [n address (pairs old-addresses)]
(let [now (. new-addresses n)]
(if (or (not now) (not (= now.len address.len)))
(table.insert deleted address))))
deleted))
(if (not (. new-addresses n))
(table.insert deleted address)))
(values added deleted)))
(fn update-prefixes [wan-device addresses new-addresses exec]
(each [_ p (ipairs (deletions addresses new-addresses))]
(exec
(.. "ip address del " p.address "1/" p.len " dev " wan-device)))
(each [_ p (pairs new-addresses)]
(exec
(.. "ip address change " p.address "1/" p.len
" dev " wan-device
" valid_lft " p.valid
" preferred_lft " p.preferred
)))
new-addresses)
(fn update-prefixes [device prefixes new-prefixes]
(let [(added deleted) (changes prefixes new-prefixes)]
;; if some address has changed (e.g. preferred/valid lifetime)
;; then we don't want to delete it before re-adding it because
;; the kernel will drop any routes that go through it. On the
;; other hand, we can't add it _before_ deleting it as we'll
;; get an error that it already exists. Therefore, use "change"
;; instead of "add", it works a bit more like an upsert
(each [_ p (ipairs added)]
(system
(.. "ip address change " p.address "1/" p.len " dev " device
" valid_lft " p.valid
" preferred_lft " p.preferred
)))
(each [_ p (ipairs deleted)]
(system
(.. "ip address del " p.address "1/" p.len " dev " device)))))
(fn run []
(let [[state-directory lan-device] arg
dir (svc.open state-directory)]
(accumulate [addresses []
v (dir:events)]
(update-prefixes lan-device addresses (v:output "prefix") system))))
(update-prefixes lan-device addresses (v:output "prefix")))))
{ : changes : run }
+2 -2
View File
@@ -2,9 +2,9 @@
writeFennel
, linotify
, anoia
, lualinux
, lua
}:
writeFennel "acquire-delegated-prefix" {
packages = [ linotify anoia lualinux ];
packages = [ linotify anoia lua.pkgs.luafilesystem ];
mainFunction = "run";
} ./acquire-delegated-prefix.fnl
+35 -91
View File
@@ -1,124 +1,68 @@
(local subject (require :acquire-wan-address))
(import-macros { : expect= } :anoia.assert)
(local { : view } (require :fennel))
(local { : merge : dup } (require :anoia))
;; nix-shell --run "cd modules/dhcp6c && fennelrepl acquire-wan-address-test.fnl"
(local a1
{
"2001-ab-cd-ef" {
:address "2001:ab:cd:ef"
:len "64"
:preferred "3600"
:valid "7200"
}
}
)
(local a156
{
"2001-ab-cd-ef" {
:address "2001:ab:cd:ef"
:len "56"
:preferred "3600"
:valid "7200"
}
"2001-ab-cd-ef_hjgKHGhKJH" {
:address "2001:ab:cd:ef"
:len "64"
:preferred "200"
:valid "200"
}
}
)
(local a2
{
"2001-0-1-2-3" {
:address "2001:0:1:2:3"
:len "64"
:preferred "3600"
:valid "7200"
}
"2001-0-1-2-3_aNteBnb" {
:address "2001:0:1:2:3"
:len "64"
:preferred "200"
:valid "200"
}
}
)
(local a21
{
"2001-0-1-2-3" {
:address "2001:0:1:2:3"
:len "64"
:preferred "1800"
:valid "5400"
}
}
)
(macro expect [assertion]
(let [msg (.. "expectation failed: " (view assertion))]
`(when (not ,assertion)
(assert false ,msg))))
(fn first-address []
(let [deleted
(subject.deletions
(let [(add del)
(subject.changes
{ }
a1
)]
(expect= deleted [])))
(expect (= (# del) 0))
(expect (= (# add) 1))
(let [[first] add]
(expect (= first.address "2001:ab:cd:ef")))))
(fn second-address []
(let [del
(subject.deletions
(let [(add del)
(subject.changes
a1
(merge (dup a1) a2)
)]
(expect= del [])))
(expect (= (# del) 0))
(expect (= (# add) 1))
(let [[first] add] (expect (= first.address "2001:0:1:2:3")))))
(fn old-address-is-deleted []
(let [del
(subject.deletions
(fn less-address []1
(let [(add del)
(subject.changes
(merge (dup a1) a2)
a1
)]
(expect= (. del 1) (. a2 "2001-0-1-2-3"))
))
(expect (= (# add) 0))
(expect (= (# del) 1))
(fn changed-lifetime-not-deleted []
(let [del
(subject.deletions
(merge (dup a1) a2)
(merge (dup a1) a21)
)]
;; when an address lifetime changes, "ip address change"
;; will update that so it need not (should not) be deleted
(expect= del [])))
(let [[first] del] (expect (= first.address "2001:0:1:2:3")))))
(fn changed-prefix-is-deleted []
(let [del
(subject.deletions a1 a156)]
;; when an address prefix changes, "ip address change"
;; ignores that cjhange, so we have to remove the
;; address before reinstating it
(expect= del [(. a1 "2001-ab-cd-ef")])))
(first-address)
(second-address)
(old-address-is-deleted)
(changed-lifetime-not-deleted)
(changed-prefix-is-deleted)
(let [cmds []]
(subject.update-addresses
"ppp0" a1 (merge (dup a1) a2)
(fn [a] (table.insert cmds a)))
(expect=
(doto cmds table.sort)
[
;; order of changes is unimportant
"ip address change 2001:0:1:2:3/64 dev ppp0 valid_lft 7200 preferred_lft 3600"
"ip address change 2001:ab:cd:ef/64 dev ppp0 valid_lft 7200 preferred_lft 3600"
]))
(let [cmds []]
(subject.update-addresses
"ppp0" (merge (dup a1) a2) a1
(fn [a] (table.insert cmds a)))
(expect=
cmds
[
;; deletes are executed before changes
"ip address del 2001:0:1:2:3/64 dev ppp0"
"ip address change 2001:ab:cd:ef/64 dev ppp0 valid_lft 7200 preferred_lft 3600"
]))
(print "OK")
(less-address)
+29 -21
View File
@@ -1,32 +1,40 @@
(local { : system } (require :anoia))
(local svc (require :anoia.svc))
(fn deletions [old-addresses new-addresses]
(let [deleted {}]
(each [n address (pairs old-addresses)]
(let [now (. new-addresses n)]
(if (or (not now) (not (= now.len address.len)))
(table.insert deleted address))))
deleted))
;; acquire-delegated-prefix has very similar code: we'd like to move
;; this to anoia.svc when we see what the general form would look like
(fn update-addresses [wan-device addresses new-addresses exec]
(each [_ p (ipairs (deletions addresses new-addresses))]
(exec
(.. "ip address del " p.address "/" p.len " dev " wan-device)))
(each [_ p (pairs new-addresses)]
(exec
(.. "ip address change " p.address "/" p.len
" dev " wan-device
" valid_lft " p.valid
" preferred_lft " p.preferred
)))
new-addresses)
(fn changes [old-addresses new-addresses]
(let [added {}
deleted {}]
(each [n address (pairs new-addresses)]
(if (not (. old-addresses n))
(table.insert added address)))
(each [n address (pairs old-addresses)]
(if (not (. new-addresses n))
(table.insert deleted address)))
(values added deleted)))
(fn update-addresses [wan-device addresses new-addresses]
(let [(added deleted) (changes addresses new-addresses)]
;; see comment in acquire-delegated-prefix.fnl
(each [_ p (ipairs added)]
(system
(.. "ip address change " p.address "/" p.len
" dev " wan-device
" valid_lft " p.valid
" preferred_lft " p.preferred
)))
(each [_ p (ipairs deleted)]
(system
(.. "ip address del " p.address "/" p.len " dev " wan-device)))
new-addresses))
(fn run []
(let [[state-directory wan-device] arg
dir (svc.open state-directory)]
(accumulate [addresses []
v (dir:events)]
(update-addresses wan-device addresses (v:output "address") system))))
(update-addresses wan-device addresses (v:output "address")))))
{ : update-addresses : deletions : run }
{ : update-addresses : changes : run }
+1 -2
View File
@@ -2,10 +2,9 @@
writeFennel
, linotify
, anoia
, lualinux
, lua
}:
writeFennel "acquire-wan-address" {
packages = [ linotify anoia lualinux ];
packages = [ linotify anoia lua.pkgs.luafilesystem ];
mainFunction = "run";
} ./acquire-wan-address.fnl
+2
View File
@@ -1,10 +1,12 @@
{
liminix
, lib
, callPackage
}:
{ client, interface } :
let
inherit (liminix.services) longrun;
inherit (lib) mkOption types;
name = "dhcp6c.addr.${client.name}.${interface.name}";
script = callPackage ./acquire-wan-address.nix { };
in longrun {
+2
View File
@@ -1,11 +1,13 @@
{
liminix
, lib
, odhcp6c
, odhcp-script
}:
{ interface } :
let
inherit (liminix.services) longrun;
inherit (lib) mkOption types;
name = "dhcp6c.${interface.name}";
in longrun {
inherit name;
+1
View File
@@ -12,6 +12,7 @@
{ lib, pkgs, config, ...}:
let
inherit (lib) mkOption types;
inherit (pkgs.liminix.services) oneshot;
inherit (pkgs) liminix;
in
{
+2
View File
@@ -1,10 +1,12 @@
{
liminix
, lib
, callPackage
}:
{ client, interface } :
let
inherit (liminix.services) longrun;
inherit (lib) mkOption types;
name = "dhcp6c.prefix.${client.name}.${interface.name}";
script = callPackage ./acquire-delegated-prefix.nix { };
in longrun {
+3 -4
View File
@@ -18,7 +18,7 @@ let
name = "${interface.name}.dnsmasq";
inherit (liminix.services) longrun;
inherit (lib) concatStrings concatStringsSep mapAttrsToList;
hostOpt = name : { mac, v4, v6, leasetime }:
hostOpt = name : { mac, v4, v6, leasetime } @ attrs:
let v6s = concatStrings (map (a : ",[${a}]") v6);
in "--dhcp-host=${mac},${v4}${v6s},${name},${builtins.toString leasetime}";
in
@@ -41,11 +41,10 @@ longrun {
--no-hosts \
--log-dhcp \
--enable-ra \
--log-debug \
--log-queries \
--log-facility=- \
--dhcp-leasefile=$(mkstate ${name})/leases \
--pid-file=/run/${name}.pid
'';
# --log-debug \
# --log-queries \
}
+3 -12
View File
@@ -8,6 +8,7 @@
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
inherit (pkgs.liminix.services) oneshot;
kmodules = pkgs.kmodloader.override {
inherit (config.system.outputs) kernel;
@@ -55,14 +56,8 @@ in
config = {
system.service.firewall =
let svc = liminix.callService ./service.nix {
extraRules = mkOption {
type = types.attrsOf types.attrs;
description = "firewall ruleset";
default = {};
};
rules = mkOption {
ruleset = mkOption {
type = types.attrsOf types.attrs; # we could usefully tighten this a bit :-)
default = import ./default-rules.nix;
description = "firewall ruleset";
};
};
@@ -73,17 +68,13 @@ in
};
in svc.build args' ;
};
programs.busybox.applets = [
"insmod" "rmmod"
];
kernel.config = {
NETFILTER = "y";
NETFILTER_ADVANCED = "y";
NETFILTER_NETLINK = "m";
NF_CONNTRACK = "m";
NETLINK_DIAG = "y";
IP6_NF_IPTABLES= "m";
IP_NF_IPTABLES = "m";
IP_NF_NAT = "m";
+4 -2
View File
@@ -4,10 +4,12 @@
, firewallgen
, nftables
}:
{ rules, extraRules }:
{ ruleset }:
let
inherit (liminix.services) oneshot;
script = firewallgen "firewall.nft" (lib.recursiveUpdate rules extraRules);
inherit (liminix.lib) typeChecked;
inherit (lib) mkOption types;
script = firewallgen "firewall.nft" ruleset;
in oneshot {
name = "firewall";
up = script;
+7 -7
View File
@@ -5,13 +5,14 @@
## you want to run on it, and would usually be set in the "device" file:
## :file:`devices/manuf-model/default.nix`
{ lib, ... }:
{ lib, pkgs, config, ...}:
let
inherit (lib) mkOption types;
in
{
inherit (lib) mkEnableOption mkOption types isDerivation hasAttr ;
in {
options = {
boot = { };
boot = {
};
hardware = {
dts = {
src = mkOption {
@@ -25,7 +26,7 @@ in
'';
};
includes = mkOption {
default = [ ];
default = [];
description = "List of directories to search for DTS includes (.dtsi files)";
type = types.listOf types.path;
};
@@ -66,7 +67,6 @@ in
};
loadAddress = mkOption { type = types.ints.unsigned; default = null; };
entryPoint = mkOption { type = types.ints.unsigned; };
alignment = mkOption { type = types.nullOr types.ints.unsigned; default = null; description = "Alignment passed to `mkimage` for FIT"; };
radios = mkOption {
description = ''
Kernel modules (from mac80211 package) required for the
+2
View File
@@ -8,6 +8,8 @@
let
inherit (liminix.services) longrun;
inherit (lib) concatStringsSep mapAttrsToList;
inherit (liminix.lib) typeChecked;
inherit (lib) mkOption types;
# This is not a friendly interface to configuring a wireless AP: it
# just passes everything straight through to the hostapd config.
-18
View File
@@ -1,18 +0,0 @@
{ config, pkgs, lib, ... } :
let
inherit (pkgs) liminix;
inherit (lib) mkOption types;
in {
options.system.service.ifwait =
mkOption { type = liminix.lib.types.serviceDefn; };
config.system.service.ifwait = config.system.callService ./ifwait.nix {
state = mkOption { type = types.str; };
interface = mkOption {
type = liminix.lib.types.interface;
};
service = mkOption {
type = liminix.lib.types.service;
};
};
}
-16
View File
@@ -1,16 +0,0 @@
{ ifwait, liminix } :
{
state
, interface
, service
}:
let
inherit (liminix.services) longrun;
in longrun {
name = "ifwait.${interface.name}";
buildInputs = [ service ];
restart-on-upgrade = true;
run = ''
${ifwait}/bin/ifwait -s ${service.name} $(output ${interface} ifname) ${state}
'';
}
+6 -1
View File
@@ -5,9 +5,14 @@
{ lib, pkgs, config, ...}:
let
inherit (lib) mkOption types ;
inherit (lib) mkEnableOption mkOption types isDerivation hasAttr ;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs.liminix.networking) address interface;
inherit (pkgs.liminix.services) bundle;
inherit (pkgs) liminix;
type_service = pkgs.liminix.lib.types.service;
mergeConditionals = conf : conditions :
# for each key in conditions, if it is present in conf
# then merge the associated value into conf
-30
View File
@@ -1,30 +0,0 @@
{ config, pkgs, ...} :
let inherit (pkgs.liminix.services) oneshot longrun;
in {
config = {
services = rec {
mdevd = longrun {
name = "mdevd";
notification-fd = 3;
run = "${pkgs.mdevd}/bin/mdevd -D 3 -b 200000 -O4";
};
devout = longrun {
name = "devout";
notification-fd = 10;
run = "exec ${pkgs.devout}/bin/devout /run/devout.sock 4";
};
coldplug = oneshot {
name = "coldplug";
# would love to know what mdevd-coldplug/udevadm trigger does
# that this doesn't
up = ''
for i in $(find /sys -name uevent); do ( echo change > $i ) ; done
'';
dependencies = [
devout
mdevd
];
};
};
};
}
+26 -32
View File
@@ -7,6 +7,11 @@
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
mkBoolOption = description : mkOption {
type = types.bool;
inherit description;
default = true;
};
in {
options = {
@@ -14,39 +19,28 @@ in {
type = liminix.lib.types.serviceDefn;
};
};
imports = [ ../mdevd.nix ../service-trigger ];
config.system.service.mount =
let svc = config.system.callService ./service.nix {
partlabel = mkOption {
type = types.str;
example = "my-usb-stick";
};
mountpoint = mkOption {
type = types.str;
example = "/mnt/media";
};
options = mkOption {
type = types.listOf types.str;
default = [];
example = ["noatime" "ro" "sync"];
};
fstype = mkOption {
type = types.str;
default = "auto";
example = "vfat";
};
};
in svc // {
build = args:
let args' = args // {
dependencies = (args.dependencies or []) ++ [
config.services.mdevd
config.services.devout
];
};
in svc.build args' ;
config.system.service = {
mount = liminix.callService ./service.nix {
device = mkOption {
type = types.str;
example = "/dev/sda1";
};
mountpoint = mkOption {
type = types.str;
example = "/mnt/media";
};
options = mkOption {
type = types.listOf types.str;
default = [];
example = ["noatime" "ro" "sync"];
};
fstype = mkOption {
type = types.str;
default = "auto";
example = "vfat";
};
};
};
config.programs.busybox = {
applets = ["blkid" "findfs"];
options = {
+9 -18
View File
@@ -1,27 +1,18 @@
{
liminix
, lib
, svc
}:
{ partlabel, mountpoint, options, fstype }:
{ device, mountpoint, options, fstype }:
let
inherit (liminix.services) oneshot;
device = "/dev/disk/by-partlabel/${partlabel}";
name = "mount.${lib.strings.sanitizeDerivationName (lib.escapeURL mountpoint)}";
options_string =
if options == [] then "" else "-o ${lib.concatStringsSep "," options}";
controller = svc.uevent-rule.build {
serviceName = name;
symlink = device;
terms = {
partname = partlabel;
devtype = "partition";
};
};
in oneshot {
inherit name;
timeout-up = 3600;
up = "mount -t ${fstype} ${options_string} ${device} ${mountpoint}";
name = "mount.${lib.escapeURL mountpoint}";
up = ''
while ! findfs ${device}; do
echo waiting for device ${device}
sleep 1
done
mount -t ${fstype} -o ${lib.concatStringsSep "," options} ${device} ${mountpoint}
'';
down = "umount ${mountpoint}";
inherit controller;
}
+1
View File
@@ -1,5 +1,6 @@
{
liminix
, ifwait
, serviceFns
, lib
}:
+2 -2
View File
@@ -17,7 +17,7 @@ let
ip address replace $ip/$mask dev $interface
(in_outputs ${name}
for i in lease mask ip router siaddr dns serverid subnet opt53 interface ; do
(printenv $i || true) > $i
printenv $i > $i
done)
}
case $action in
@@ -40,7 +40,7 @@ let
'';
in longrun {
inherit name;
run = "exec /bin/udhcpc -f -i $(output ${interface} ifname) -x hostname:$(cat /proc/sys/kernel/hostname) -s ${script}";
run = "/bin/udhcpc -f -i $(output ${interface} ifname) -x hostname:$(cat /proc/sys/kernel/hostname) -s ${script}";
notification-fd = 10;
dependencies = [ interface ];
}
+2
View File
@@ -1,5 +1,7 @@
{
liminix
, ifwait
, serviceFns
, lib
}:
{ enableIPv4, enableIPv6 }:
+4 -1
View File
@@ -1,5 +1,7 @@
{
liminix
, ifwait
, serviceFns
, lib
}:
{
@@ -9,7 +11,8 @@
# if devpath is supplied, we rename the interface at that
# path to have the specified name.
let
inherit (liminix.services) oneshot;
inherit (liminix.services) longrun oneshot;
inherit (lib) concatStringsSep;
name = "${ifname}.link";
rename = if devpath != null
then ''
+3 -3
View File
@@ -1,15 +1,15 @@
{
liminix
, ifwait
, serviceFns
, lib
}:
{ target, via, interface ? null, metric }:
let
inherit (liminix.services) oneshot;
with_dev = if interface != null then "dev $(output ${interface} ifname)" else "";
target_hash = builtins.substring 0 12 (builtins.hashString "sha256" target);
via_hash = builtins.substring 0 12 (builtins.hashString "sha256" via);
in oneshot {
name = "route-${target_hash}-${builtins.substring 0 12 (builtins.hashString "sha256" "${via_hash}-${if interface!=null then interface.name else ""}")}";
name = "route-${target}-${builtins.substring 0 12 (builtins.hashString "sha256" "${via}-${if interface!=null then interface.name else ""}")}";
up = ''
ip route add ${target} via ${via} metric ${toString metric} ${with_dev}
'';
+5
View File
@@ -1,6 +1,7 @@
{
liminix
, chrony
, serviceFns
, lib
, writeText
}:
@@ -8,6 +9,10 @@ params:
let
inherit (liminix.services) longrun;
inherit (lib) concatStringsSep mapAttrsToList;
inherit (liminix.lib) typeChecked;
inherit (lib) mkOption types;
serverOpts = types.listOf types.str;
configFile = p:
(mapAttrsToList (name: opts: "server ${name} ${concatStringsSep "" opts}")
p.servers)
+10 -11
View File
@@ -1,12 +1,12 @@
{
config,
pkgs,
lib,
...
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkOption types concatStringsSep;
inherit (pkgs) liminix writeText;
inherit (pkgs) liminix callPackage writeText;
o = config.system.outputs;
in
{
@@ -22,7 +22,7 @@ in
# but only part of one.
kernel = mkOption {
type = types.package;
internal = true;
internal = true;
description = ''
kernel
******
@@ -42,7 +42,7 @@ in
};
dtb = mkOption {
type = types.package;
internal = true;
internal = true;
description = ''
dtb
***
@@ -52,7 +52,7 @@ in
};
uimage = mkOption {
type = types.package;
internal = true;
internal = true;
description = ''
uimage
******
@@ -68,7 +68,7 @@ in
};
manifest = mkOption {
type = types.package;
internal = true;
internal = true;
description = ''
Debugging aid. JSON rendition of config.filesystem, on
which can run "nix-store -q --tree" on it and find
@@ -111,8 +111,7 @@ in
};
uimage = liminix.builders.uimage {
commandLine = concatStringsSep " " config.boot.commandLine;
inherit (config.boot) commandLineDtbNode;
inherit (config.hardware) loadAddress entryPoint alignment;
inherit (config.hardware) loadAddress entryPoint;
inherit (config.boot) imageFormat;
inherit (o) kernel dtb;
};
+1 -1
View File
@@ -5,7 +5,7 @@
, ...
}:
let
inherit (lib) mkIf;
inherit (lib) mkIf mkOption types;
o = config.system.outputs;
in
{
+1 -1
View File
@@ -5,7 +5,7 @@
, ...
}:
let
inherit (lib) mkIf;
inherit (lib) mkIf mkOption types;
o = config.system.outputs;
in
{
+1 -1
View File
@@ -6,7 +6,7 @@
}:
let
inherit (lib) mkEnableOption mkOption mkIf types;
inherit (pkgs) runCommand;
inherit (pkgs) runCommand callPackage writeText;
in
{
options = {
+1 -1
View File
@@ -5,7 +5,7 @@
, ...
}:
let
inherit (lib) mkIf;
inherit (lib) mkIf mkOption types;
o = config.system.outputs;
in
{
+3 -2
View File
@@ -5,7 +5,7 @@
, ...
}:
let
inherit (lib) mkOption types concatStringsSep;
inherit (lib) mkOption mkForce types concatStringsSep;
in {
imports = [ ../ramdisk.nix ];
options.system.outputs = {
@@ -42,7 +42,8 @@ in {
boot-sh =
let
inherit (config.system.outputs) rootfs;
inherit (pkgs.lib.trivial) toHexString;
inherit (config.system.outputs) rootfs kernel;
cmdline = concatStringsSep " " config.boot.commandLine;
in
pkgs.buildPackages.runCommand "boot.sh.sh" {
+1 -1
View File
@@ -5,7 +5,7 @@
, ...
}:
let
inherit (lib) mkOption types;
inherit (lib) mkOption types concatStringsSep;
o = config.system.outputs;
phram_address = lib.toHexString (config.hardware.ram.startAddress + 256 * 1024 * 1024);
in {
+4 -3
View File
@@ -8,7 +8,6 @@ let
inherit (lib) mkOption types concatStringsSep;
cfg = config.boot.tftp;
hw = config.hardware;
arch = pkgs.stdenv.hostPlatform.linuxArch;
in {
imports = [ ../ramdisk.nix ];
options.boot.tftp = {
@@ -58,6 +57,7 @@ in {
system.outputs = rec {
tftpboot =
let
inherit (pkgs.lib.trivial) toHexString;
o = config.system.outputs;
image = let choices = {
uimage = o.uimage;
@@ -121,7 +121,7 @@ in {
fdtput -p -t lx dtb /reserved-memory/$node reg $ac_prefix $(hex $rootfsStart) $sz_prefix $(hex $rootfsSize)
cmd="liminix ${cmdline} mtdparts=phram0:''${rootfsSize}(rootfs) phram.phram=phram0,''${rootfsStart},''${rootfsSize},${toString config.hardware.flash.eraseBlockSize} root=/dev/mtdblock0";
fdtput -t s dtb /chosen ${config.boot.commandLineDtbNode} "$cmd"
fdtput -t s dtb /chosen bootargs "$cmd"
dtbSize=$(binsize ./dtb )
@@ -131,7 +131,8 @@ in {
cat ${o.kernel} > vmlinux.elf
${objcopy} --update-section .appended_dtb=dtb vmlinux.elf
${stripAndZip}
mkimage -A ${arch} -O linux -T kernel -C lzma -a $(hex ${toString hw.loadAddress}) -e $(hex ${toString hw.entryPoint}) -n '${lib.toUpper arch} Liminix Linux tftpboot' -d vmlinux.bin.lzma image
# TODO don't hardcode mips, entryPoint, loadAddress, name
mkimage -A mips -O linux -T kernel -C lzma -a $(hex ${toString hw.loadAddress}) -e $(hex ${toString hw.entryPoint}) -n 'MIPS Liminix Linux' -d vmlinux.bin.lzma image
# dtc -I dtb -O dts -o /dev/stdout dtb | grep -A10 chosen ; exit 1
tftpcmd="tftpboot $(hex $imageStart) result/image "
bootcmd="bootm $(hex $imageStart)"
+1 -1
View File
@@ -5,7 +5,7 @@
, ...
}:
let
inherit (lib) mkOption types;
inherit (lib) mkOption types concatStringsSep;
o = config.system.outputs;
cfg = config.tplink-safeloader;
in {
+3 -10
View File
@@ -12,16 +12,9 @@ in
imports = [
./initramfs.nix
];
options.system.outputs.rootubifs = mkOption {
type = types.package;
internal = true;
};
options.hardware.ubi = {
minIOSize = mkOption { type = types.str; };
logicalEraseBlockSize = mkOption { type = types.str; }; # LEB
physicalEraseBlockSize = mkOption { type = types.str; }; # PEB
eraseBlockSize = mkOption { type = types.str; }; # LEB
maxLEBcount = mkOption { type = types.str; }; # LEB
};
@@ -33,7 +26,7 @@ in
};
boot.initramfs.enable = true;
system.outputs = {
rootubifs =
rootfs =
let
inherit (pkgs.pkgsBuildBuild) runCommand mtdutils;
cfg = config.hardware.ubi;
@@ -42,7 +35,7 @@ in
} ''
mkdir tmp
tree=${o.bootablerootdir}
mkfs.ubifs -x favor_lzo -c ${cfg.maxLEBcount} -m ${cfg.minIOSize} -e ${cfg.logicalEraseBlockSize} -y -r $tree --output $out --squash-uids -o $out
mkfs.ubifs -x favor_lzo -c ${cfg.maxLEBcount} -m ${cfg.minIOSize} -e ${cfg.eraseBlockSize} -y -r $tree --output $out --squash-uids -o $out
'';
};
};
+1 -1
View File
@@ -5,7 +5,7 @@
, ...
}:
let
inherit (lib) mkIf mkOption types;
inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep;
cfg = config.boot.tftp;
instructions = pkgs.writeText "env.scr" ''
setenv serverip ${cfg.serverip}
-90
View File
@@ -1,90 +0,0 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkIf mkOption types concatStringsSep optionalString;
in
{
imports = [
./initramfs.nix
./ubifs.nix
];
options.hardware.ubi = {
minIOSize = mkOption { type = types.str; };
eraseBlockSize = mkOption { type = types.str; }; # LEB
maxLEBcount = mkOption { type = types.str; }; # LEB
};
config = mkIf (config.rootfsType == "ubifs") {
kernel.config = {
MTD_UBI="y";
UBIFS_FS = "y";
UBIFS_FS_SECURITY = "n";
};
boot.initramfs.enable = true;
system.outputs.rootfs =
let
inherit (pkgs.pkgsBuildBuild) runCommand;
ubiVolume = ({ name, volumeId, image, flags ? [] }:
''
[${name}]
mode=ubi
vol_id=${toString volumeId}
vol_type=dynamic
vol_name=${name}
vol_alignment=1
${optionalString (image != null) ''
image=${image}
''}
${optionalString (image == null) ''
vol_size=1MiB
''}
${optionalString (flags != []) ''
vol_flags=${concatStringsSep "," flags}
''}
'');
ubiImage = (volumes:
let
ubinizeConfig = pkgs.writeText "ubinize.conf" (concatStringsSep "\n" volumes);
inherit (pkgs.pkgsBuildBuild) mtdutils;
in
runCommand "ubinize" {
depsBuildBuild = [ mtdutils ];
# block size := 128kb
# page size := 2048
# ubninize opts := -E 5
} ''
ubinize -Q "$SOURCE_DATE_EPOCH" -o $out \
-p ${config.hardware.ubi.physicalEraseBlockSize} -m ${config.hardware.ubi.minIOSize} \
-e ${config.hardware.ubi.logicalEraseBlockSize} \
${ubinizeConfig}
'');
ubiDisk = ({ initramfs }:
let
initramfsUbi = ubiVolume {
name = "rootfs";
volumeId = 0;
image = initramfs;
flags = [ "autoresize" ];
};
in
ubiImage [
initramfsUbi
]);
disk = ubiDisk {
initramfs = config.system.outputs.rootubifs; # liminix.builders.squashfs config.filesystem.contents; # # assert this is a proper FIT.
};
in
disk;
};
}
-71
View File
@@ -1,71 +0,0 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkIf mkOption types;
models = "6b e1 6f e1 ff ff ff ff ff ff";
in {
options.system.outputs = {
zyxel-nwa-fit = mkOption {
type = types.package;
description = ''
zyxel-nwa-fit
*************
This output provides a FIT image for Zyxel NWA series
containing a kernel image and an UBIFS rootfs.
It can usually be used as a factory image to install Liminix
on a system with pre-existing firmware and OS.
'';
};
};
imports = [
./ubivolume.nix
];
config = mkIf (config.rootfsType == "ubifs") {
system.outputs.zyxel-nwa-fit =
let
o = config.system.outputs;
# 8129kb padding.
paddedKernel = pkgs.runCommand "padded-kernel" {} ''
cp --no-preserve=mode ${o.uimage} $out
dd if=/dev/zero of=$out bs=1 count=1 seek=8388607
'';
firmwareImage = pkgs.runCommand "firmware-image" {} ''
cat ${paddedKernel} ${o.rootfs} > $out
'';
dts = pkgs.writeText "image.its" ''
/dts-v1/;
/ {
description = "Zyxel FIT (Flattened Image Tree)";
compat-models = [${models}];
#address-cells = <1>;
images {
firmware {
data = /incbin/("${firmwareImage}");
type = "firmware";
compression = "none";
hash@1 {
algo = "sha1";
};
};
};
};
'';
in
pkgs.runCommand "zyxel-nwa-fit-${config.boot.imageType}" {
nativeBuildInputs = [ pkgs.pkgsBuildBuild.ubootTools pkgs.pkgsBuildBuild.dtc ];
} ''
mkimage -f ${dts} $out
'';
};
}
-15
View File
@@ -17,9 +17,6 @@ in {
system.service.pppoe = mkOption {
type = liminix.lib.types.serviceDefn;
};
system.service.l2tp = mkOption {
type = liminix.lib.types.serviceDefn;
};
};
config = {
system.service.pppoe = pkgs.liminix.callService ./pppoe.nix {
@@ -32,16 +29,6 @@ in {
description = "options supplied on ppp command line";
};
};
system.service.l2tp = pkgs.liminix.callService ./l2tp.nix {
lns = mkOption {
type = types.str;
description = "hostname or address of the L2TP network server";
};
ppp-options = mkOption {
type = types.listOf types.str;
description = "options supplied on ppp command line";
};
};
kernel = {
config = {
PPP = "y";
@@ -49,8 +36,6 @@ in {
PPP_DEFLATE = "y";
PPP_ASYNC = "y";
PPP_SYNC_TTY = "y";
PPPOL2TP = "y";
L2TP = "y";
};
};
};
-61
View File
@@ -1,61 +0,0 @@
{
liminix
, writeAshScript
, writeText
, serviceFns
, xl2tpd
} :
{ lns, ppp-options }:
let
inherit (liminix.services) longrun;
name = "${lns}.l2tp";
ip-up = writeAshScript "ip-up" {} ''
. ${serviceFns}
(in_outputs ${name}
echo $1 > ifname
echo $2 > tty
echo $3 > speed
echo $4 > address
echo $5 > peer-address
echo $DNS1 > ns1
echo $DNS2 > ns2
)
echo >/proc/self/fd/10
'';
ip6-up = writeAshScript "ip6-up" {} ''
. ${serviceFns}
(in_outputs ${name}
echo $4 > ipv6-address
echo $5 > ipv6-peer-address
)
echo >/proc/self/fd/10
'';
ppp-options' = ppp-options ++ [
"ip-up-script" ip-up
"ipv6-up-script" ip6-up
"ipparam" name
"nodetach"
"usepeerdns"
"logfd" "2"
];
conf = writeText "xl2tpd.conf" ''
[lac upstream]
lns = ${lns}
require authentication = no
pppoptfile = ${writeText "ppp-options" ppp-options'}
autodial = yes
redial = no
max redials = 1
'';
control = "/run/xl2tpd/control-${name}";
in
longrun {
inherit name;
run = ''
mkdir -p /run/xl2tpd
touch ${control}
exec ${xl2tpd}/bin/xl2tpd -D -p /run/xl2tpd/${name}.pid -c ${conf} -C ${control}
'';
notification-fd = 10;
timeout-up = 40 * 1000;
}
+2 -8
View File
@@ -9,8 +9,6 @@
{ interface, ppp-options }:
let
inherit (liminix.services) longrun;
lcp-echo-interval = 4;
lcp-echo-failure = 3;
name = "${interface.name}.pppoe";
ip-up = writeAshScript "ip-up" {} ''
. ${serviceFns}
@@ -39,19 +37,15 @@ let
"ipparam" name
"nodetach"
"usepeerdns"
"lcp-echo-interval" (builtins.toString lcp-echo-interval)
"lcp-echo-failure" (builtins.toString lcp-echo-failure)
"logfd" "2"
];
in
longrun {
inherit name;
run = ''
. ${serviceFns}
echo Starting pppoe, pppd pid is $$
exec ${ppp}/bin/pppd pty "${pppoe}/bin/pppoe -T ${builtins.toString (4 * lcp-echo-interval)} -I $(output ${interface} ifname)" ${lib.concatStringsSep " " ppp-options'}
. ${serviceFns}
${ppp}/bin/pppd pty "${pppoe}/bin/pppoe -I $(output ${interface} ifname)" ${lib.concatStringsSep " " ppp-options'}
'';
notification-fd = 10;
timeout-up = (10 + lcp-echo-failure * lcp-echo-interval) * 1000;
dependencies = [ interface ];
}
-178
View File
@@ -1,178 +0,0 @@
{ config, pkgs, lib, ... } :
let
svc = config.system.service;
cfg = config.profile.gateway;
inherit (lib) mkOption mkEnableOption mkIf types;
inherit (pkgs) liminix serviceFns;
inherit (liminix.services) bundle oneshot;
hostaps =
let
defaults = {
auth_algs = 1; # 1=wpa2, 2=wep, 3=both
wpa = 2; # 1=wpa, 2=wpa2, 3=both
wpa_key_mgmt = "WPA-PSK";
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
rsn_pairwise = "CCMP"; # auth for wpa2
};
in lib.mapAttrs'
(name : value :
let
attrs = defaults // { ssid = name; } // value;
in lib.nameValuePair
"hostap-${name}"
(svc.hostapd.build {
interface = attrs.interface;
params = lib.filterAttrs (k: v: k != "interface") attrs;
}))
cfg.wireless.networks;
in {
options.profile.gateway = {
lan = {
interfaces = mkOption {
type = types.listOf liminix.lib.types.interface;
default = [];
};
address = mkOption {
type = types.attrs;
};
prefix = mkOption { type = types.str; };
dhcp = {
start = mkOption { type = types.int; };
end = mkOption { type = types.int; };
hosts = mkOption { type = types.attrs; };
localDomain = mkOption { type = types.str; };
};
};
firewall = {
enable = mkEnableOption "firewall";
rules = mkOption { type = types.attrsOf types.attrs; };
};
wan = {
interface = mkOption { type = liminix.lib.types.interface; };
username = mkOption { type = types.str; };
password = mkOption { type = types.str; };
dhcp6.enable = mkOption { type = types.bool; };
};
wireless = mkOption {
type = types.attrsOf types.anything;
};
};
imports = [
../wlan.nix
../network
../ppp
../dnsmasq
../dhcp6c
../firewall
../hostapd
../bridge
../ntp
../ssh
{ config.services = hostaps; }
];
config = {
services.int = svc.network.address.build ({
interface = svc.bridge.primary.build { ifname = "int"; };
} // cfg.lan.address);
services.bridge = svc.bridge.members.build {
primary = config.services.int;
members = cfg.lan.interfaces;
};
services.wan = svc.pppoe.build {
inherit (cfg.wan) interface;
ppp-options = [
"debug" "+ipv6" "noauth"
"name" cfg.wan.username
"password" cfg.wan.password
];
};
services.packet_forwarding = svc.network.forward.build { };
services.dhcp6c =
let
client = svc.dhcp6c.client.build {
interface = config.services.wan;
};
bundl = bundle {
name = "dhcp6c";
contents = [
(svc.dhcp6c.prefix.build {
inherit client;
interface = config.services.int;
})
(svc.dhcp6c.address.build {
inherit client;
interface = config.services.wan;
})
];
};
in mkIf cfg.wan.dhcp6.enable bundl;
services.dns =
let interface = config.services.int;
dcfg = cfg.lan.dhcp;
in svc.dnsmasq.build {
resolvconf = config.services.resolvconf;
inherit interface;
ranges = [
"${cfg.lan.prefix}.${toString dcfg.start},${cfg.lan.prefix}.${toString dcfg.end}"
# ra-stateless: sends router advertisements with the O and A
# bits set, and provides a stateless DHCP service. The client
# will use a SLAAC address, and use DHCP for other
# configuration information.
"::,constructor:$(output ${interface} ifname),ra-stateless"
];
hosts = dcfg.hosts;
upstreams = [ "/${dcfg.localDomain}/" ];
domain = dcfg.localDomain;
};
services.defaultroute4 = svc.network.route.build {
via = "$(output ${config.services.wan} address)";
target = "default";
dependencies = [ config.services.wan ];
};
services.defaultroute6 = svc.network.route.build {
via = "$(output ${config.services.wan} ipv6-peer-address)";
target = "default";
interface = config.services.wan;
};
services.firewall = mkIf cfg.firewall.enable
(svc.firewall.build {
extraRules = cfg.firewall.rules;
});
services.resolvconf = oneshot rec {
dependencies = [ config.services.wan ];
name = "resolvconf";
up = ''
. ${serviceFns}
( in_outputs ${name}
echo "nameserver $(output ${config.services.wan} ns1)" > resolv.conf
echo "nameserver $(output ${config.services.wan} ns2)" >> resolv.conf
chmod 0444 resolv.conf
)
'';
};
filesystem =
let inherit (pkgs.pseudofile) dir symlink;
in dir {
etc = dir {
"resolv.conf" = symlink "${config.services.resolvconf}/.outputs/resolv.conf";
};
};
};
}
-98
View File
@@ -1,98 +0,0 @@
{
config,
pkgs,
lib,
...
}: let
inherit (pkgs) liminix;
inherit (lib) mkOption types ;
inherit (pkgs.liminix.services) oneshot target;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) serviceFns;
svc = config.system.service;
cfg = config.profile.wap;
hostaps =
let
defaults = {
auth_algs = 1; # 1=wpa2, 2=wep, 3=both
wpa = 2; # 1=wpa, 2=wpa2, 3=both
wpa_key_mgmt = "WPA-PSK";
wpa_pairwise = "TKIP CCMP"; # auth for wpa (may not need this?)
rsn_pairwise = "CCMP"; # auth for wpa2
};
in lib.mapAttrs'
(name : value :
let
attrs = defaults // { ssid = name; } // value;
in lib.nameValuePair
"hostap-${name}"
(svc.hostapd.build {
interface = attrs.interface;
params = lib.filterAttrs (k: v: k != "interface") attrs;
}))
cfg.wireless.networks;
in {
imports = [
../wlan.nix
../network
../hostapd
../bridge
{ config.services = hostaps; }
];
options.profile.wap = {
interfaces = mkOption {
type = types.listOf liminix.lib.types.interface;
default = [];
};
wireless = mkOption {
type = types.attrsOf types.anything;
};
};
config = {
services.int = svc.bridge.primary.build {
ifname = "int";
};
services.bridge = svc.bridge.members.build {
primary = config.services.int;
members = cfg.interfaces;
};
services.dhcpc = svc.network.dhcp.client.build {
interface = config.services.int;
dependencies = [ config.services.hostname ];
};
services.defaultroute4 = svc.network.route.build {
via = "$(output ${config.services.dhcpc} router)";
target = "default";
dependencies = [config.services.dhcpc];
};
services.resolvconf = oneshot rec {
dependencies = [ config.services.dhcpc ];
name = "resolvconf";
# CHECK: https://udhcp.busybox.net/README.udhcpc says
# 'A list of DNS server' but doesn't say what separates the
# list members. Assuming it's a space or other IFS character
up = ''
. ${serviceFns}
( in_outputs ${name}
for i in $(output ${config.services.dhcpc} dns); do
echo "nameserver $i" > resolv.conf
done
)
'';
};
filesystem = dir {
etc = dir {
"resolv.conf" = symlink "${config.services.resolvconf}/.outputs/resolv.conf";
};
};
};
}
+7 -2
View File
@@ -1,6 +1,11 @@
{ config, lib, ... }:
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkIf mkEnableOption; # types concatStringsSep;
inherit (lib) mkIf mkEnableOption mkOption; # types concatStringsSep;
in {
options = {
boot = {
+3 -39
View File
@@ -1,4 +1,4 @@
{ config, pkgs, lib, ... }:
{ config, pkgs, ... }:
let
inherit (pkgs)
execline
@@ -6,50 +6,14 @@ let
s6-init-bin
s6-linux-init
stdenvNoCC;
inherit (lib.lists) unique concatMap;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs.liminix.services) oneshot bundle;
inherit (pkgs.liminix.services) bundle;
s6-rc-db =
let
# In the default bundle we need to have all the services
# in config.services except for controlled services and
# anything that depends on one. But we do need the controllers
# themselves.
# So, find all required services and their transitive
# dependencies and their controllers. remove all controlled
# services and all services that have a controlled service as
# dependency
isControlled = s : s ? controller && s.controller != null;
deps = s : s.dependencies ++
lib.optional (isControlled s) s.controller;
flatDeps = s : [s] ++ concatMap flatDeps (deps s);
allServices = unique (concatMap flatDeps (builtins.attrValues config.services));
isDependentOnControlled = s :
isControlled s ||
(lib.lists.any isDependentOnControlled s.dependencies);
# all controlled services depend on this oneshot, which
# makes a list of them so we can identify them at runtime
controlled = oneshot {
name = "controlled";
up = ''
mkdir -p /run/services/controlled
for s in $(s6-rc-db -d dependencies controlled); do
touch /run/services/controlled/$s
done
'';
down = "rm -r /run/services/controlled";
};
defaultStart =
builtins.filter
(s: !(isDependentOnControlled s)) allServices;
defaultDefaultTarget = bundle {
name = "default";
contents = defaultStart ++ [controlled];
contents = builtins.attrValues config.services;
};
servicesAttrs = {
default = defaultDefaultTarget;
+1 -1
View File
@@ -34,7 +34,7 @@ fi
### If your services are managed by s6-rc:
### (replace /run/service with your scandir)
s6-rc-init -d -c /etc/s6-rc/compiled /run/service
s6-rc-init /run/service -d -c /etc/s6-rc/compiled
### 2. Starting the wanted set of services
+1 -1
View File
@@ -1,4 +1,4 @@
{ config, pkgs, ... } :
{ config, pkgs, lib, ... } :
{
config = {
programs.busybox = {
-37
View File
@@ -1,37 +0,0 @@
# this is unlikely to be the final form or location of this code, it's
# an interim module which wraps the uevent-watch command
{ lib, pkgs, config, ... }:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
# inherit (pkgs.liminix.services) bundle;
in {
options = {
system.service.uevent-rule = mkOption {
description = "a service which starts other services based on device state (sysfs)";
type = liminix.lib.types.serviceDefn;
};
};
config = {
system.service.uevent-rule = liminix.callService ./rule.nix {
serviceName = mkOption {
description = "name of the service to run when the rule matches";
type = types.str;
};
terms = mkOption {
type = types.attrs;
example = {
devtype = "usb_device";
attrs.idVendor = "8086";
};
default = {};
};
symlink = mkOption {
description = "create symlink targeted on devpath";
type = types.nullOr types.str;
default = null;
};
};
};
}
-23
View File
@@ -1,23 +0,0 @@
{
liminix
, uevent-watch
, lib }:
{
serviceName, terms, symlink
}:
let
inherit (liminix.services) longrun;
inherit (lib.attrsets) collect mapAttrsRecursive;
inherit (lib.strings) concatStringsSep;
stringify = attrs :
concatStringsSep " "
(collect lib.isString
(mapAttrsRecursive
(path : value : "${concatStringsSep "." path}=${value}")
attrs));
termsString = stringify terms;
in longrun {
name = "watch-for-${serviceName}";
restart-on-upgrade = true;
run = "${uevent-watch}/bin/uevent-watch ${if symlink != null then "-n ${symlink}" else ""} -s ${serviceName} ${termsString}";
}
+4 -4
View File
@@ -1,8 +1,8 @@
{
config,
pkgs,
lib,
...
config
, pkgs
, lib
, ...
}:
let
inherit (pkgs) liminix;
+1
View File
@@ -1,6 +1,7 @@
{
liminix
, dropbear
, serviceFns
, lib
}:
p :

Some files were not shown because too many files have changed in this diff Show More