Compare commits
25 Commits
main
...
gateway-pr
Author | SHA1 | Date |
---|---|---|
Daniel Barlow | 67080627a1 | |
Daniel Barlow | 269c9cd916 | |
Daniel Barlow | 95ebddb661 | |
Daniel Barlow | a66510c9e6 | |
Daniel Barlow | 75aa01513c | |
Daniel Barlow | d3fd6f5c0a | |
Daniel Barlow | 90302f0944 | |
Daniel Barlow | 00bf3446f1 | |
Daniel Barlow | 0d8abbc314 | |
Daniel Barlow | 4c3883a5e7 | |
Daniel Barlow | 2ecaa3bef8 | |
Daniel Barlow | 5dd595a209 | |
Daniel Barlow | 0b61c0b318 | |
Daniel Barlow | 78efd6dfcf | |
Daniel Barlow | 77421a5d3b | |
Daniel Barlow | 74f4577521 | |
Daniel Barlow | 3ec5245fa4 | |
Daniel Barlow | c223e5cea2 | |
Daniel Barlow | 921be2f3c8 | |
Daniel Barlow | da64f64ee9 | |
Daniel Barlow | c46e228c60 | |
Daniel Barlow | 0386452ddc | |
Daniel Barlow | 4c549ac625 | |
Daniel Barlow | 4d0061e90a | |
Daniel Barlow | af966056c7 |
53
THOUGHTS.txt
53
THOUGHTS.txt
|
@ -4289,3 +4289,56 @@ Wed Mar 6 18:24:29 GMT 2024
|
|||
What happens when we attempt to start the service but it fails? We
|
||||
assume the start was successful so we won't try and restart it again
|
||||
next time we get an event that should cause it to start.
|
||||
|
||||
Thu Mar 7 11:48:26 GMT 2024
|
||||
|
||||
what next?
|
||||
|
||||
- fennel script needs to know where s6-rc is
|
||||
- some nix syntax
|
||||
- update bridge module members.nix to use the new thing
|
||||
|
||||
I can't find a ci derivation that uses the bridge.
|
||||
|
||||
Mon Mar 11 20:31:45 GMT 2024
|
||||
|
||||
Create a qemu config where wan and lan devices are bridged into a
|
||||
single bridge
|
||||
|
||||
start qemu paused
|
||||
Use qemu monitor commands to no-carrier the network devices
|
||||
set_link virtio-net-pci.1 off
|
||||
set_link virtio-net-pci.0 off
|
||||
|
||||
Boot the system
|
||||
|
||||
See if both devices are bridge members
|
||||
|
||||
See if reboot is possible
|
||||
|
||||
Use qemu monitor commands to enable the network devices
|
||||
set_link virtio-net-pci.1 on
|
||||
set_link virtio-net-pci.0 on
|
||||
|
||||
See if both devices are bridge members
|
||||
|
||||
disable again,check if back to starting position
|
||||
|
||||
|
||||
Wed Mar 13 00:00:16 GMT 2024
|
||||
|
||||
aside: "trigger" is the least bad word I've thought of so far for
|
||||
these services that stop/start other services
|
||||
|
||||
telent: yeah, in general 'ps afuxww' (or s6-ps -H :)) is the way to solve this, look for hung s6-rc processes and in particular their s6-svlisten1 children, where the command line will show what service is still waiting for readiness
|
||||
|
||||
Sat Mar 16 23:28:10 GMT 2024
|
||||
|
||||
We have the gl-ar750 booting with the bridge members added by trigger, and
|
||||
it seems to work.
|
||||
|
||||
One thing worth flagging is that we have to run s6-rc with the -b
|
||||
(block if locked) flag in the trigger otherwise the triggered service
|
||||
just doesn't come up if there's something else starting at the same
|
||||
time. There are warnings in the s6-rc docs about doing this, though.
|
||||
We need to think hard about whether we can end up calling s6-rc recursively
|
||||
|
|
|
@ -20,6 +20,9 @@ let
|
|||
});
|
||||
|
||||
eval = pkgs.lib.evalModules {
|
||||
specialArgs = {
|
||||
modulesPath = builtins.toString ./modules;
|
||||
};
|
||||
modules = [
|
||||
{ _module.args = { inherit pkgs; inherit (pkgs) lim; }; }
|
||||
./modules/hardware.nix
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
config,
|
||||
pkgs,
|
||||
lib,
|
||||
modulesPath,
|
||||
...
|
||||
}: let
|
||||
secrets = import ./extneder-secrets.nix;
|
||||
svc = config.system.service;
|
||||
in rec {
|
||||
boot = {
|
||||
tftp = {
|
||||
|
@ -20,47 +22,13 @@ in rec {
|
|||
};
|
||||
|
||||
imports = [
|
||||
../modules/profiles/wap.nix
|
||||
../modules/vlan
|
||||
"${modulesPath}/profiles/wap.nix"
|
||||
"${modulesPath}/vlan"
|
||||
"${modulesPath}/ssh"
|
||||
];
|
||||
|
||||
hostname = "extneder";
|
||||
|
||||
kernel = {
|
||||
config = {
|
||||
|
||||
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";
|
||||
};
|
||||
};
|
||||
|
||||
profile.wap = {
|
||||
interfaces = with config.hardware.networkInterfaces; [
|
||||
lan
|
||||
|
@ -79,6 +47,7 @@ in rec {
|
|||
};
|
||||
};
|
||||
|
||||
services.sshd = svc.ssh.build {};
|
||||
users.root.passwd = lib.mkForce secrets.root.passwd;
|
||||
defaultProfile.packages = with pkgs; [nftables strace tcpdump swconfig];
|
||||
}
|
||||
|
|
|
@ -6,23 +6,18 @@
|
|||
# problems.
|
||||
|
||||
|
||||
{ config, pkgs, lib, ... } :
|
||||
{ config, pkgs, lib, modulesPath, ... } :
|
||||
let
|
||||
secrets = {
|
||||
domainName = "fake.liminix.org";
|
||||
firewallRules = {};
|
||||
} // (import ./rotuer-secrets.nix);
|
||||
inherit (pkgs.liminix.services) oneshot longrun bundle;
|
||||
inherit (pkgs.liminix.services) oneshot bundle;
|
||||
inherit (pkgs) serviceFns;
|
||||
svc = config.system.service;
|
||||
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;
|
||||
};
|
||||
|
||||
|
@ -36,65 +31,64 @@ in rec {
|
|||
};
|
||||
|
||||
imports = [
|
||||
../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
|
||||
"${modulesPath}/profiles/gateway.nix"
|
||||
"${modulesPath}/schnapps"
|
||||
"${modulesPath}/outputs/btrfs.nix"
|
||||
"${modulesPath}/outputs/extlinux.nix"
|
||||
];
|
||||
hostname = "rotuer";
|
||||
rootfsType = "btrfs";
|
||||
rootOptions = "subvol=@";
|
||||
boot.loader.extlinux.enable = true;
|
||||
|
||||
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
|
||||
];
|
||||
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 =
|
||||
let defaults = import ./demo-firewall.nix;
|
||||
in lib.recursiveUpdate defaults 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.ntp = svc.ntp.build {
|
||||
|
@ -106,95 +100,6 @@ 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
|
||||
|
|
|
@ -29,6 +29,10 @@ in {
|
|||
services = mkOption {
|
||||
type = types.attrsOf type_service;
|
||||
};
|
||||
system.callService = mkOption {
|
||||
type = types.functionTo (types.functionTo types.anything);
|
||||
};
|
||||
|
||||
filesystem = mkOption {
|
||||
type = types.anything;
|
||||
description = ''
|
||||
|
@ -111,6 +115,31 @@ 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 = (builtins.map (d: d.name) dependencies) ++ o.dependencies;
|
||||
buildInputs = dependencies ++ o.buildInputs;
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
users.root = {
|
||||
uid = 0; gid= 0; gecos = "Root of all evaluation";
|
||||
dir = "/home/root/";
|
||||
|
|
|
@ -14,6 +14,8 @@ let
|
|||
inherit (pkgs) liminix;
|
||||
in
|
||||
{
|
||||
imports = [ ../ifwait ];
|
||||
|
||||
options = {
|
||||
system.service.bridge = {
|
||||
primary = mkOption { type = liminix.lib.types.serviceDefn; };
|
||||
|
@ -27,7 +29,7 @@ in
|
|||
description = "bridge interface name to create";
|
||||
};
|
||||
};
|
||||
members = liminix.callService ./members.nix {
|
||||
members = config.system.callService ./members.nix {
|
||||
primary = mkOption {
|
||||
type = liminix.lib.types.interface;
|
||||
description = "primary bridge interface";
|
||||
|
@ -47,5 +49,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";
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
liminix
|
||||
, ifwait
|
||||
, lib
|
||||
, svc
|
||||
}:
|
||||
{ members, primary } :
|
||||
|
||||
|
@ -10,14 +11,20 @@ let
|
|||
inherit (liminix.services) bundle oneshot;
|
||||
inherit (lib) mkOption types;
|
||||
addif = 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";
|
||||
# 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;
|
||||
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";
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
{ 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;
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
{ ifwait, liminix } :
|
||||
{
|
||||
state
|
||||
, interface
|
||||
, service
|
||||
}:
|
||||
let
|
||||
inherit (liminix.services) longrun;
|
||||
in longrun {
|
||||
name = "ifwait.${interface.name}";
|
||||
buildInputs = [ service ];
|
||||
run = ''
|
||||
${ifwait}/bin/ifwait -s ${service.name} $(output ${interface} ifname) ${state}
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
{ config, pkgs, lib, ... } :
|
||||
let
|
||||
svc = config.system.service;
|
||||
cfg = config.profile.gateway;
|
||||
inherit (lib) mkOption mkEnableOption mkIf mdDoc types optional optionals;
|
||||
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 {
|
||||
ruleset = 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";
|
||||
};
|
||||
};
|
||||
};
|
||||
# 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;
|
||||
# };
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -40,7 +40,6 @@ in {
|
|||
../network
|
||||
../hostapd
|
||||
../bridge
|
||||
../ssh
|
||||
{ config.services = hostaps; }
|
||||
];
|
||||
|
||||
|
@ -54,7 +53,6 @@ in {
|
|||
};
|
||||
};
|
||||
config = {
|
||||
services.sshd = svc.ssh.build {};
|
||||
|
||||
services.int = svc.bridge.primary.build {
|
||||
ifname = "int";
|
||||
|
|
16
overlay.nix
16
overlay.nix
|
@ -77,6 +77,22 @@ extraPkgs // {
|
|||
|
||||
};
|
||||
|
||||
# luarocks wants a cross-compiled cmake (which seems like a bug,
|
||||
# we're never going to run luarocks on the device, but ...)
|
||||
# but https://github.com/NixOS/nixpkgs/issues/284734
|
||||
# so we do surgery on the cmake derivation until that's fixed
|
||||
|
||||
cmake = prev.cmake.overrideAttrs(o:
|
||||
# don't override the build cmake or we'll have to rebuild
|
||||
# half the known universe to no useful benefit
|
||||
if final.stdenv.buildPlatform != final.stdenv.hostPlatform
|
||||
then {
|
||||
preConfigure =
|
||||
builtins.replaceStrings
|
||||
["$configureFlags"] ["$configureFlags $cmakeFlags"] o.preConfigure;
|
||||
}
|
||||
else {}
|
||||
);
|
||||
|
||||
dnsmasq =
|
||||
let d = prev.dnsmasq.overrideAttrs(o: {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
(local netlink (require :netlink))
|
||||
|
||||
(local { : view } (require :fennel))
|
||||
; (local { : view } (require :fennel))
|
||||
|
||||
(fn events [groups]
|
||||
(let [sock (netlink.socket)]
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
, netlink-lua
|
||||
, writeFennelScript
|
||||
, runCommand
|
||||
, anoia
|
||||
}:
|
||||
runCommand "ifwait" {} ''
|
||||
mkdir -p $out/bin
|
||||
cp -p ${writeFennelScript "ifwait" [netlink-lua] ./ifwait.fnl} $out/bin/ifwait
|
||||
cp -p ${writeFennelScript "ifwait" [anoia netlink-lua] ./ifwait.fnl} $out/bin/ifwait
|
||||
''
|
||||
|
|
|
@ -37,8 +37,8 @@
|
|||
(when (not (= up wanted?))
|
||||
(set up
|
||||
(if wanted?
|
||||
(pcall system (.. "s6-rc -u change " service))
|
||||
(not (pcall system (.. "s6-rc -d change " service)))))
|
||||
(pcall system (.. "s6-rc -b -u change " service))
|
||||
(not (pcall system (.. "s6-rc -b -d change " service)))))
|
||||
))
|
||||
|
||||
(fn run [args event-fn]
|
||||
|
|
|
@ -11,7 +11,7 @@ test -n "$contents" && for d in $contents; do
|
|||
touch $out/${name}/contents.d/$d
|
||||
done
|
||||
|
||||
for i in run notification-fd up down consumer-for producer-for pipeline-name ; do
|
||||
for i in timeout-up timeout-down run notification-fd up down consumer-for producer-for pipeline-name ; do
|
||||
test -n "$(printenv $i)" && (echo "$(printenv $i)" > $out/${name}/$i)
|
||||
done
|
||||
|
||||
|
|
|
@ -31,6 +31,8 @@ let
|
|||
, producer-for ? null
|
||||
, consumer-for ? null
|
||||
, pipeline-name ? null
|
||||
, timeout-up ? 30000 # milliseconds
|
||||
, timeout-down ? 0
|
||||
, dependencies ? []
|
||||
, contents ? []
|
||||
, buildInputs ? []
|
||||
|
@ -39,7 +41,7 @@ let
|
|||
# we use stdenvNoCC to avoid generating derivations with names
|
||||
# like foo.service-mips-linux-musl
|
||||
inherit name serviceType up down run notification-fd
|
||||
producer-for consumer-for pipeline-name;
|
||||
producer-for consumer-for pipeline-name timeout-up timeout-down;
|
||||
buildInputs = buildInputs ++ dependencies ++ contents;
|
||||
dependencies = builtins.map (d: d.name) dependencies;
|
||||
contents = builtins.map (d: d.name) contents;
|
||||
|
@ -52,6 +54,7 @@ let
|
|||
, outputs ? []
|
||||
, notification-fd ? null
|
||||
, dependencies ? []
|
||||
, buildInputs ? []
|
||||
, ...
|
||||
} @ args:
|
||||
let logger = service {
|
||||
|
@ -63,7 +66,7 @@ let
|
|||
pipeline-name = "${name}-pipeline";
|
||||
};
|
||||
in service (args // {
|
||||
buildInputs = [ logger ];
|
||||
buildInputs = buildInputs ++ [ logger ];
|
||||
serviceType = "longrun";
|
||||
run = serviceScript "${run}\n${cleanupScript name}";
|
||||
producer-for = "${name}-log";
|
||||
|
|
|
@ -8,4 +8,5 @@
|
|||
min-copy-closure = import ./min-copy-closure/test.nix;
|
||||
fennel = import ./fennel/test.nix;
|
||||
tftpboot = import ./tftpboot/test.nix;
|
||||
updown = import ./updown/test.nix;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
{ config, pkgs, lib, ... } :
|
||||
let
|
||||
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.
|
||||
ipv4LocalNet = "10.8.0";
|
||||
svc = config.system.service;
|
||||
|
||||
in rec {
|
||||
imports = [
|
||||
../../modules/bridge
|
||||
../../modules/dhcp6c
|
||||
../../modules/dnsmasq
|
||||
../../modules/firewall
|
||||
../../modules/hostapd
|
||||
../../modules/network
|
||||
../../modules/ssh
|
||||
../../modules/vlan
|
||||
../../modules/wlan.nix
|
||||
];
|
||||
rootfsType = "jffs2";
|
||||
hostname = "updown";
|
||||
|
||||
services.int = svc.network.address.build {
|
||||
interface = svc.bridge.primary.build { ifname = "int"; };
|
||||
family = "inet"; address = "${ipv4LocalNet}.1"; prefixLength = 16;
|
||||
};
|
||||
|
||||
services.bridge = svc.bridge.members.build {
|
||||
primary = services.int;
|
||||
members = with config.hardware.networkInterfaces;
|
||||
[ lan ];
|
||||
};
|
||||
|
||||
services.sshd = svc.ssh.build { };
|
||||
|
||||
# users.root = {
|
||||
# # EDIT: choose a root password and then use
|
||||
# # "mkpasswd -m sha512crypt" to determine the hash.
|
||||
# # It should start wirh $6$.
|
||||
# passwd = "$6$6HG7WALLQQY1LQDE$428cnouMJ7wVmyK9.dF1uWs7t0z9ztgp3MHvN5bbeo0M4Kqg/u2ThjoSHIjCEJQlnVpDOaEKcOjXAlIClHWN21";
|
||||
# openssh.authorizedKeys.keys = [
|
||||
# # EDIT: you can add your ssh pubkey here
|
||||
# # "ssh-rsa AAAAB3NzaC1....H6hKd user@example.com";
|
||||
# ];
|
||||
# };
|
||||
|
||||
defaultProfile.packages = with pkgs; [
|
||||
min-collect-garbage
|
||||
# strace
|
||||
# ethtool
|
||||
tcpdump
|
||||
];
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
set timeout 10
|
||||
|
||||
spawn socat unix-connect:vm/monitor -
|
||||
set monitor_id $spawn_id
|
||||
|
||||
expect "(qemu)"
|
||||
send "set_link virtio-net-pci.1 off\n"
|
||||
expect "(qemu)"
|
||||
send "set_link virtio-net-pci.0 off\n"
|
||||
expect "(qemu)"
|
||||
send "c\r\n"
|
||||
spawn socat unix-connect:vm/console -
|
||||
set console_id $spawn_id
|
||||
|
||||
expect "BusyBox"
|
||||
expect "#" { send "PS1=RE\\ADY_\\ ; stty -echo \r" }
|
||||
expect "READY_" { send "s6-rc -b -a list\r" } ; # -b waits for s6-rc lock
|
||||
expect "READY_" { send "ls /sys/class/net/lan/master\r" }
|
||||
expect {
|
||||
"No such file or directory" { }
|
||||
timeout { exit 1 }
|
||||
}
|
||||
|
||||
expect "READY_" { send "cat /sys/class/net/lan/operstate\r" }
|
||||
expect {
|
||||
"down" { }
|
||||
"up" { exit 1 }
|
||||
}
|
||||
expect "READY_" { send "s6-rc -a -u change\r" }
|
||||
expect {
|
||||
"unable to take locks" { exit 1 }
|
||||
"READY_" { send "\r" }
|
||||
}
|
||||
|
||||
set spawn_id $monitor_id
|
||||
send "\r"
|
||||
expect "(qemu)"
|
||||
send "set_link virtio-net-pci.1 on\n"
|
||||
expect "(qemu)"
|
||||
send "set_link virtio-net-pci.0 on\n"
|
||||
expect "(qemu)"
|
||||
set spawn_id $console_id
|
||||
|
||||
expect "entered forwarding state"
|
||||
send "\r"
|
||||
expect "READY_" { send "cat /sys/class/net/lan/operstate\r" }
|
||||
expect {
|
||||
"down" { exit 1 }
|
||||
"up" { }
|
||||
}
|
||||
|
||||
expect "READY_" { send "cat /sys/class/net/lan/master/uevent\r" }
|
||||
expect {
|
||||
"INTERFACE=int" { }
|
||||
timeout { exit 1 }
|
||||
}
|
||||
|
||||
expect "READY_" { send "s6-rc listall int.link.a.10.8.0.1.member.lan.link ; hostname\r" }
|
||||
|
||||
expect {
|
||||
"updown" {}
|
||||
timeout { exit 1 }
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
liminix
|
||||
, nixpkgs
|
||||
}:
|
||||
let img = (import liminix {
|
||||
device = import "${liminix}/devices/qemu/";
|
||||
liminix-config = ./configuration.nix;
|
||||
}).outputs.vmroot;
|
||||
pkgs = import <nixpkgs> { overlays = [(import ../../overlay.nix)]; };
|
||||
in pkgs.runCommand "check" {
|
||||
nativeBuildInputs = with pkgs; [
|
||||
expect
|
||||
socat
|
||||
] ;
|
||||
} ''
|
||||
mkdir vm
|
||||
${img}/run.sh --flag -S --background ./vm
|
||||
expect ${./script.expect} | tee $out
|
||||
''
|
Loading…
Reference in New Issue