Compare commits

..

No commits in common. "e1ae986cf66c396f6d5970735abd2042c99ead1f" and "df395a4d5da80d6f89257ed921674bff434367c6" have entirely different histories.

12 changed files with 133 additions and 273 deletions

12
NEWS
View File

@ -103,15 +103,3 @@ a bit more useful :-)
};
})
];
2024-07-16
* structured parameters are available for the pppoe service
* The "wan" configuration in modules/profiles/gateway.nix has changed:
instead of passing options that are used to create a pppoe interface,
callers should create a (pppoe or other) interface and pass that as
the value of profile.gateway.wan. For the pppoe case this is now only
very slightly more verbose, and it allows using the gateway profile
with other kinds of upstream.

View File

@ -5400,24 +5400,3 @@ generalising the failover example:
- usb stick may or may not need a modeswitch
- may need a different chat script
- usb ids
Mon Jul 15 17:52:57 BST 2024
DONE 1) Should round-robin be a callService service or a function a la
longrun/oneshot, or even an overridable package?
DONE 2) maybe we should replace all liminix.callService with
config.system.callService
3) for consistency, can we make the networking "primitives" into
services? answer: no. the only thing left there is `ifup` which is a
function returning a string, not a derivation
Tue Jul 16 18:25:41 BST 2024
can we make the gateway profile able to use failover? perhaps if we
add username and password as options to the pppoe service, then call
gateway with the pppoe service instead of building it _in_ the profile,
we can have gateways with other-than-pppoe for the wan
(for a straight lte uplink, could pass the wwan interface as wan)

View File

@ -30,11 +30,6 @@
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) serviceFns;
svc = config.system.service;
wirelessConfig = {
country_code = "GB";
inherit (rsecrets) wpa_passphrase;
wmm_enabled = 1;
};
in rec {
boot = {
tftp = {
@ -46,14 +41,12 @@ in rec {
imports = [
../modules/wwan
../modules/network
# ../modules/vlan
../modules/vlan
../modules/ssh
../modules/usb.nix
# ../modules/watchdog
# ../modules/mount
../modules/watchdog
../modules/mount
../modules/ppp
../modules/round-robin
../modules/profiles/gateway.nix
];
hostname = "thing";
@ -64,35 +57,27 @@ in rec {
authType = "chap";
};
profile.gateway = {
lan = {
interfaces = with config.hardware.networkInterfaces;
[
# EDIT: these are the interfaces exposed by the gl.inet gl-ar750:
# if your device has more or differently named lan interfaces,
# specify them here
wlan wlan5
lan
];
inherit (rsecrets.lan) prefix;
address = {
family = "inet"; address ="${rsecrets.lan.prefix}.1"; prefixLength = 24;
services.wan =
let
controller = longrun rec {
name = "wan-switcher";
run = ''
in_outputs ${name}
exec ${pkgs.s6-rc-round-robin}/bin/s6-rc-round-robin \
-p ${proxy.name} \
${lib.concatStringsSep " "
(builtins.map (f: f.name) [pppoe l2tp])}
'';
};
dhcp = {
start = 10;
end = 240;
hosts = { } // lib.optionalAttrs (builtins.pathExists ./static-leases.nix) (import ./static-leases.nix);
localDomain = "lan";
};
};
wan = {
interface = let
pppoe = svc.pppoe.build {
pppoe = (svc.pppoe.build {
interface = config.hardware.networkInterfaces.wan;
debug = true;
username = rsecrets.l2tp.name;
password = rsecrets.l2tp.password;
};
ppp-options = [
"debug" "+ipv6" "noauth"
"name" rsecrets.l2tp.name
"password" rsecrets.l2tp.password
];
}).overrideAttrs(o: { inherit controller; });
l2tp =
let
@ -102,11 +87,11 @@ in rec {
dependencies = [ services.lns-address ];
};
route = svc.network.route.build {
via = "$(output ${services.bootstrap-dhcpc} router)";
via = "$(output ${services.dhcpc} router)";
target = lns.address;
dependencies = [services.bootstrap-dhcpc check-address];
dependencies = [services.dhcpc check-address];
};
in svc.l2tp.build {
in (svc.l2tp.build {
lns = lns.address;
ppp-options = [
"debug" "+ipv6" "noauth"
@ -114,51 +99,57 @@ in rec {
"password" rsecrets.l2tp.password
];
dependencies = [config.services.lns-address route check-address];
}).overrideAttrs(o: { inherit controller; });
proxy = oneshot rec {
name = "wan-proxy";
inherit controller;
buildInputs = [ pppoe l2tp];
up = ''
echo start proxy ${name}
set -x
(in_outputs ${name}
cp -rv $(output_path ${controller} active)/* .
)
'';
};
in svc.round-robin.build {
name = "wan";
services = [ l2tp pppoe ];
};
dhcp6.enable = true;
};
in proxy;
wireless.networks = {
"${rsecrets.ssid}" = {
interface = config.hardware.networkInterfaces.wlan;
hw_mode = "g";
channel = "6";
ieee80211n = 1;
} // wirelessConfig;
"${rsecrets.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.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.bootstrap-dhcpc = svc.network.dhcp.client.build {
services.dhcpc = svc.network.dhcp.client.build {
interface = config.services.wwan;
dependencies = [ config.services.hostname ];
};
services.sshd = svc.ssh.build { };
services.lns-address = let
ns = "$(output_word ${services.bootstrap-dhcpc} dns 1)";
ns = "$(output_word ${services.dhcpc} dns 1)";
route-to-bootstrap-nameserver = svc.network.route.build {
via = "$(output ${services.bootstrap-dhcpc} router)";
via = "$(output ${services.dhcpc} router)";
target = ns;
dependencies = [services.bootstrap-dhcpc];
dependencies = [services.dhcpc];
};
in oneshot rec {
name = "resolve-l2tp-server";
dependencies = [ services.bootstrap-dhcpc route-to-bootstrap-nameserver ];
dependencies = [ services.dhcpc route-to-bootstrap-nameserver ];
up = ''
(in_outputs ${name}
DNSCACHEIP="${ns}" ${pkgs.s6-dns}/bin/s6-dnsip4 ${lns.hostname} \
@ -167,13 +158,18 @@ in rec {
'';
};
# services.ntp = svc.ntp.build {
# pools = { "pool.ntp.org" = ["iburst"]; };
# makestep = { threshold = 1.0; limit = 3; };
# dependencies = with config.services; [ defaultroute4 defaultroute6 ];
# };
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.wan} peer-address)";
target = "default";
dependencies = [services.wan];
};
users.root = rsecrets.root;
# defaultProfile.packages = [ pkgs.go-l2tp ];
users.root = {
passwd = lib.mkForce secrets.root.passwd;
openssh.authorizedKeys.keys = secrets.root.keys;
};
programs.busybox.options = {
FEATURE_FANCY_TAIL = "y";

View File

@ -1,8 +1,10 @@
# This is an example that uses the "gateway" profile to create a
# "typical home wireless router" configuration suitable for a Gl.inet
# gl-ar750 router. It should be fairly simple to edit it for other
# devices: mostly you will need to attend to the number of wlan and lan
# interfaces
# This is not part of Liminix per se. This is my "scratchpad"
# configuration for the device I'm testing with.
#
# Parts of it do do things that Liminix eventually needs to do, but
# don't look in here for solutions - just for identifying the
# problems.
{ config, pkgs, lib, modulesPath, ... } :
let
@ -28,18 +30,21 @@ in rec {
imports = [
"${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;
profile.gateway = {
lan = {
interfaces = with config.hardware.networkInterfaces;
[
# EDIT: these are the interfaces exposed by the gl.inet gl-ar750:
# if your device has more or differently named lan interfaces,
# specify them here
wlan wlan5
lan
lan0 lan1 lan2 lan3 lan4
];
inherit (secrets.lan) prefix;
address = {
@ -53,17 +58,9 @@ in rec {
};
};
wan = {
# wan interface depends on your upstream - could be dhcp, static
# ethernet, a pppoe, ppp over serial, a complicated bonded
# failover ... who knows what else?
interface = svc.pppoe.build {
interface = config.hardware.networkInterfaces.wan;
username = secrets.l2tp.name;
password = secrets.l2tp.password;
};
# once the wan has ipv4 connnectivity, should we run dhcp6
# client to potentially get an address range ("prefix
# delegation")
dhcp6.enable = true;
};
firewall = {
@ -71,10 +68,6 @@ in rec {
rules = secrets.firewallRules;
};
wireless.networks = {
# EDIT: if you have more or fewer wireless radios, here is where
# you need to say so. hostapd tuning is hardware-specific and
# left as an exercise for the reader :-).
"${secrets.ssid}" = {
interface = config.hardware.networkInterfaces.wlan;
hw_mode = "g";

View File

@ -12,8 +12,6 @@
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
mkStringOption =
description: mkOption { type = types.str; inherit description; };
in {
options = {
system.service.pppoe = mkOption {
@ -29,34 +27,9 @@ in {
type = liminix.lib.types.service;
description = "ethernet interface to run PPPoE over";
};
username = mkStringOption "username";
password = mkStringOption "password";
lcpEcho = {
adaptive = mkOption {
description = "send LCP echo-request frames only if no traffic was received from the peer since the last echo-request was sent";
type = types.bool;
default = true;
};
interval = mkOption {
type = types.nullOr types.int;
default = 3;
description = "send an LCP echo-request frame to the peer every n seconds";
};
failure = mkOption {
type = types.nullOr types.int;
default = 3;
description = "terminate connection if n LCP echo-requests are sent without receiving a valid LCP echo-reply";
};
};
debug = mkOption {
description = "log the contents of all control packets sent or received";
default = false;
type = types.bool;
};
ppp-options = mkOption {
type = types.listOf types.str;
description = "options supplied on ppp command line";
default = [];
};
};
system.service.l2tp = config.system.callService ./l2tp.nix {

View File

@ -6,16 +6,11 @@
, writeAshScript
, serviceFns
} :
{ interface,
ppp-options,
lcpEcho,
username,
password,
debug
}:
{ interface, ppp-options }:
let
inherit (liminix.services) longrun;
inherit (lib) optional optionals;
lcp-echo-interval = 4;
lcp-echo-failure = 3;
name = "${interface.name}.pppoe";
ip-up = writeAshScript "ip-up" {} ''
. ${serviceFns}
@ -38,35 +33,25 @@ let
)
echo >/proc/self/fd/10
'';
ppp-options' = ["+ipv6" "noauth"]
++ optional debug "debug"
++ optionals (username != null) ["name" username]
++ optionals (password != null) ["password" password]
++ optional lcpEcho.adaptive "lcp-echo-adaptive"
++ optionals (lcpEcho.interval != null)
["lcp-echo-interval" (builtins.toString lcpEcho.interval)]
++ optionals (lcpEcho.failure != null)
["lcp-echo-failure" (builtins.toString lcpEcho.failure)]
++ ppp-options
++ ["ip-up-script" ip-up
ppp-options' = ppp-options ++ [
"ip-up-script" ip-up
"ipv6-up-script" ip6-up
"ipparam" name
"nodetach"
"usepeerdns"
"lcp-echo-interval" (builtins.toString lcp-echo-interval)
"lcp-echo-failure" (builtins.toString lcp-echo-failure)
"logfd" "2"
];
timeoutOpt = if lcpEcho.interval != null then "-T ${builtins.toString (4 * lcpEcho.interval)}" else "";
in
longrun {
inherit name;
run = ''
. ${serviceFns}
echo Starting pppoe, pppd pid is $$
exec ${ppp}/bin/pppd pty "${pppoe}/bin/pppoe ${timeoutOpt} -I $(output ${interface} ifname)" ${lib.concatStringsSep " " ppp-options'}
exec ${ppp}/bin/pppd pty "${pppoe}/bin/pppoe -T ${builtins.toString (4 * lcp-echo-interval)} -I $(output ${interface} ifname)" ${lib.concatStringsSep " " ppp-options'}
'';
notification-fd = 10;
timeout-up = if lcpEcho.failure != null
then (10 + lcpEcho.failure * lcpEcho.interval) * 1000
else 60 * 1000;
timeout-up = (10 + lcp-echo-failure * lcp-echo-interval) * 1000;
dependencies = [ interface ];
}

View File

@ -52,6 +52,8 @@ in {
wan = {
interface = mkOption { type = liminix.lib.types.interface; };
username = mkOption { type = types.str; };
password = mkOption { type = types.str; };
dhcp6.enable = mkOption { type = types.bool; };
};
@ -84,7 +86,14 @@ in {
members = cfg.lan.interfaces;
};
services.wan = cfg.wan.interface;
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 { };

View File

@ -1,27 +0,0 @@
## Round Robin
##
## Given a list of services, run each in turn until it exits, then
## runs the next.
{ lib, pkgs, config, ...}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
inherit (pkgs.liminix.services) longrun;
in {
options = {
system.service.round-robin = mkOption {
description = "run services one at a time and failover to next";
type = liminix.lib.types.serviceDefn;
};
};
config.system.service.round-robin = config.system.callService ./service.nix {
services = mkOption {
type = types.listOf liminix.lib.types.service;
};
name = mkOption {
type = types.str;
};
};
}

View File

@ -1,32 +0,0 @@
{
liminix, lib, s6-rc-round-robin
}:
{ services, name} :
let
inherit (liminix.services) oneshot longrun;
controlled-services = builtins.map
(s: s.overrideAttrs(o: { inherit controller; }))
services;
controller = let name' = "control-${name}"; in longrun {
name = name';
run = ''
in_outputs ${name'}
exec ${s6-rc-round-robin}/bin/s6-rc-round-robin \
-p ${proxy.name} \
${lib.concatStringsSep " "
(builtins.map (f: f.name) controlled-services)}
'';
};
proxy = oneshot rec {
inherit name;
inherit controller;
buildInputs = controlled-services;
up = ''
echo start proxy ${name}
set -x
(in_outputs ${name}
cp -rv $(output_path ${controller} active)/* .
)
'';
};
in proxy

View File

@ -9,7 +9,6 @@ let
in {
imports = [
../service-trigger
../mdevd.nix
];
options = {
@ -24,9 +23,6 @@ in {
USB_SERIAL = "y";
USB_SERIAL_OPTION = "y";
};
programs.busybox.applets = [
"insmod" "rmmod"
];
# https://www.0xf8.org/2017/01/flashing-a-huawei-e3372h-4g-lte-stick-from-hilink-to-stick-mode/
system.service.wwan.huawei-e3372 =

View File

@ -31,7 +31,7 @@ let
indent = text : indentLines 0 (splitString "\n" text);
dochain = { name, type, family, rules,
dochain = { name, type, rules,
policy ? null,
priority ? "filter",
hook ? null } : ''

View File

@ -13,7 +13,7 @@ stdenv.mkDerivation {
propagatedBuildInputs = [ s6-rc-up-tree ];
installPhase = ''
mkdir -p $out/bin
cp -p ${writeFennel "s6-rc-round-robin" {
cp -p ${writeFennel "uevent-watch" {
packages = [fennel anoia linotify lualinux s6-rc-up-tree] ;
mainFunction = "run";
} ./robin.fnl} $out/bin/s6-rc-round-robin