Compare commits
No commits in common. "e1ae986cf66c396f6d5970735abd2042c99ead1f" and "df395a4d5da80d6f89257ed921674bff434367c6" have entirely different histories.
e1ae986cf6
...
df395a4d5d
12
NEWS
12
NEWS
|
@ -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.
|
||||
|
||||
|
|
21
THOUGHTS.txt
21
THOUGHTS.txt
|
@ -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)
|
||||
|
|
|
@ -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,101 +57,99 @@ 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
|
||||
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])}
|
||||
'';
|
||||
};
|
||||
pppoe = (svc.pppoe.build {
|
||||
interface = config.hardware.networkInterfaces.wan;
|
||||
|
||||
ppp-options = [
|
||||
"debug" "+ipv6" "noauth"
|
||||
"name" rsecrets.l2tp.name
|
||||
"password" rsecrets.l2tp.password
|
||||
];
|
||||
inherit (rsecrets.lan) prefix;
|
||||
address = {
|
||||
family = "inet"; address ="${rsecrets.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 = let
|
||||
pppoe = svc.pppoe.build {
|
||||
interface = config.hardware.networkInterfaces.wan;
|
||||
debug = true;
|
||||
username = rsecrets.l2tp.name;
|
||||
password = rsecrets.l2tp.password;
|
||||
};
|
||||
}).overrideAttrs(o: { inherit 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.bootstrap-dhcpc} router)";
|
||||
target = lns.address;
|
||||
dependencies = [services.bootstrap-dhcpc check-address];
|
||||
};
|
||||
in svc.l2tp.build {
|
||||
lns = lns.address;
|
||||
ppp-options = [
|
||||
"debug" "+ipv6" "noauth"
|
||||
"name" rsecrets.l2tp.name
|
||||
"password" rsecrets.l2tp.password
|
||||
];
|
||||
dependencies = [config.services.lns-address route check-address];
|
||||
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 ];
|
||||
};
|
||||
in svc.round-robin.build {
|
||||
name = "wan";
|
||||
services = [ l2tp pppoe ];
|
||||
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
|
||||
"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)/* .
|
||||
)
|
||||
'';
|
||||
};
|
||||
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";
|
||||
|
|
|
@ -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")
|
||||
interface = config.hardware.networkInterfaces.wan;
|
||||
username = secrets.l2tp.name;
|
||||
password = secrets.l2tp.password;
|
||||
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";
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
"ipv6-up-script" ip6-up
|
||||
"ipparam" name
|
||||
"nodetach"
|
||||
"usepeerdns"
|
||||
"logfd" "2"
|
||||
];
|
||||
timeoutOpt = if lcpEcho.interval != null then "-T ${builtins.toString (4 * lcpEcho.interval)}" else "";
|
||||
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"
|
||||
];
|
||||
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 ];
|
||||
}
|
||||
|
|
|
@ -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 { };
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
};
|
||||
}
|
|
@ -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
|
|
@ -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 =
|
||||
|
|
|
@ -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 } : ''
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue