Compare commits

...

11 Commits

Author SHA1 Message Date
Daniel Barlow 617355773e explain relationship with NixWRT 2022-09-25 21:21:27 +01:00
Daniel Barlow 5d51d15b86 working pppoe with readiness notification on ip-up 2022-09-25 21:12:50 +01:00
Daniel Barlow 47f8fa9797 write down things I'll forget otherwise 2022-09-25 21:11:34 +01:00
Daniel Barlow a34a3ade08 make udhcp wait for its interface to be ready 2022-09-25 21:10:57 +01:00
Daniel Barlow 3677ab9cb3 move service state out of s6 scandir
getting weird permission errors, seems simpler all raound just to
put them somewhere else
2022-09-25 21:10:04 +01:00
Daniel Barlow 1d9f39bf87 support notification-fd file in longrun 2022-09-25 21:06:08 +01:00
Daniel Barlow 92e42f1d45 fix oneshot "up" and "down" files
they're supposed to be names of executables, not text of
shell scripts
2022-09-25 21:02:10 +01:00
Daniel Barlow 01aa423dad pkgs.writeAshScript is similar to writeShellScript but busybox 2022-09-25 20:50:20 +01:00
Daniel Barlow 3540f594b1 address service: rename addr->address & add prefixLength
these names are consistent with nixos
2022-09-25 16:51:13 +01:00
Daniel Barlow 0e73362a8c expose pkgs in default derivation 2022-09-25 13:18:26 +01:00
Daniel Barlow 0c25983c0b fix kconfig override
the default kconfig should be in a config stanza, not in the
default attr of the options stanza, otherwise it gets overridden
completely instead of merged
2022-09-25 13:17:21 +01:00
13 changed files with 145 additions and 63 deletions

View File

@ -17,6 +17,34 @@ of "limen", or "of the threshold". Your router stands at the threshold
of your (online) home and everything you send to/receive from the of your (online) home and everything you send to/receive from the
outside word goes across it. outside word goes across it.
### What about NixWRT?
This is an in-progress rewrite of NixWRT, incorporating Lessons
Learned. That said, as of today (September 2022) it is not yet
anywhere _near_ feature parity.
Liminix will eventually provide these differentiators over NixWRT:
* a writable filesystem so that software updates or reconfiguration
(e.g. changing passwords) don't require taking the device offline to
reflash it.
* more flexible service management with dependencies, to allow
configurations such as "route through PPPoE if it is healthy, with
fallback to LTE"
* a spec for valid configuration options (a la NixOS module options)
to that we can detect errors at evaluation time instead of producing
a bad image.
* a network-based mechanism for secrets management so that changes can
be pushed from a central location to several Liminix devices at once
* send device metrics and logs to a monitoring/alerting/o11y
infrastructure
Today though, it does approximately none of these things and certainly
not on real hardware.
## Building ## Building

View File

@ -69,3 +69,16 @@ Fri Sep 23 10:27:22 BST 2022
* mcast=230.0.0.1:1234 : access (interconnect between router and isp) * mcast=230.0.0.1:1234 : access (interconnect between router and isp)
* mcast=230.0.0.1:1235 : lan * mcast=230.0.0.1:1235 : lan
* mcast=230.0.0.1:1236 : world (the internet) * mcast=230.0.0.1:1236 : world (the internet)
Sun Sep 25 20:56:28 BST 2022
1) shutdown doesn't work as its using the busybox one not s6
2) think we shouldn't have process-based services like dhcp, ppp
implement "address provider interface" - instead have a separate
service for interface address that depends on the service and uses its
output
* ppp is not like dhcp because dhcp finds addresses for an existing
interface but ppp makes a new one

View File

@ -5,7 +5,8 @@ let
overlay = import ./overlay.nix; overlay = import ./overlay.nix;
nixpkgs = import <nixpkgs> ( device.system // {overlays = [overlay]; }); nixpkgs = import <nixpkgs> ( device.system // {overlays = [overlay]; });
config = (import ./merge-modules.nix) [ config = (import ./merge-modules.nix) [
(import ./modules/base.nix { inherit device; }) ./modules/base.nix
({ lib, ... } : { config = { inherit (device) kernel; }; })
<liminix-config> <liminix-config>
] nixpkgs.pkgs; ] nixpkgs.pkgs;
finalConfig = config // { finalConfig = config // {
@ -15,7 +16,7 @@ let
; ;
}; };
squashfs = (import ./make-image.nix) nixpkgs finalConfig; squashfs = (import ./make-image.nix) nixpkgs finalConfig;
kernel = (import ./make-kernel.nix) nixpkgs finalConfig; kernel = (import ./make-kernel.nix) nixpkgs finalConfig.kernel.config;
in { in {
outputs = { outputs = {
inherit squashfs kernel; inherit squashfs kernel;
@ -26,4 +27,7 @@ in {
ln -s ${kernel.vmlinux} vmlinux ln -s ${kernel.vmlinux} vmlinux
''; '';
}; };
# this is just here as a convenience, so that we can get a
# cross-compiling nix-shell for any package we're customizing
inherit (nixpkgs) pkgs;
} }

View File

@ -24,6 +24,6 @@ in
{ {
vmlinux = callPackage ./make-vmlinux.nix { vmlinux = callPackage ./make-vmlinux.nix {
inherit tree; inherit tree;
inherit (config.kernel) config;# checkedConfig; inherit config;
}; };
} }

View File

@ -1,4 +1,3 @@
{ device } :
{ lib, ...}: { lib, ...}:
let inherit (lib) mkEnableOption mkOption types; let inherit (lib) mkEnableOption mkOption types;
in { in {
@ -11,7 +10,6 @@ in {
}; };
kernel = mkOption { kernel = mkOption {
type = types.anything; type = types.anything;
default = { inherit (device.kernel) config checkedConfig; };
}; };
}; };
} }

View File

@ -3,6 +3,8 @@ final: prev: {
s6-init-files = final.callPackage ./pkgs/s6-init-files {}; s6-init-files = final.callPackage ./pkgs/s6-init-files {};
strace = prev.strace.override { libunwind = null; }; strace = prev.strace.override { libunwind = null; };
liminix = final.callPackage ./pkgs/liminix-tools {}; liminix = final.callPackage ./pkgs/liminix-tools {};
writeAshScript = final.callPackage ./pkgs/write-ash-script {};
pppoe = prev.rpPPPoE.overrideAttrs (o: { pppoe = prev.rpPPPoE.overrideAttrs (o: {
# use newer rp-pppoe, it builds cleanly # use newer rp-pppoe, it builds cleanly
src = final.fetchFromGitHub { src = final.fetchFromGitHub {

View File

@ -11,6 +11,7 @@ test -n "$contents" && for d in $contents; do
touch $out/${name}/contents.d/$d touch $out/${name}/contents.d/$d
done done
test -n "$run" && (echo -e "#!$shell\n$run" > $out/${name}/run) test -n "$run" && (echo -e "#!$shell\n$run" > $out/${name}/run)
test -n "${notificationFd}" && (echo ${notificationFd} > $out/${name}/notification-fd)
test -n "$up" && (echo -e "#!$shell\n$up" > $out/${name}/up) test -n "$up" && (echo -e "#!$shell\n$up" > $out/${name}/up)
test -n "$down" && (echo -e "#!$shell\n$down" > $out/${name}/down) test -n "$down" && (echo -e "#!$shell\n$down" > $out/${name}/down)
for i in $out/${name}/{down,up,run} ; do test -f $i && chmod +x $i; done for i in $out/${name}/{down,up,run} ; do test -f $i && chmod +x $i; done

View File

@ -4,12 +4,14 @@
, lib , lib
, busybox , busybox
, callPackage , callPackage
, writeAshScript
} :let } :let
inherit (builtins) concatStringsSep; inherit (builtins) concatStringsSep;
longrun = { longrun = {
name name
, run , run
, outputs ? [] , outputs ? []
, notification-fd ? null
, dependencies ? [] , dependencies ? []
} @ args: stdenvNoCC.mkDerivation { } @ args: stdenvNoCC.mkDerivation {
name = "${name}.service"; name = "${name}.service";
@ -18,6 +20,7 @@
dependencies = builtins.map (d: d.name) dependencies; dependencies = builtins.map (d: d.name) dependencies;
shell = "${busybox}/bin/sh"; shell = "${busybox}/bin/sh";
inherit run; inherit run;
notificationFd = notification-fd;
builder = ./builder.sh; builder = ./builder.sh;
}; };
oneshot = { oneshot = {
@ -37,7 +40,9 @@
# store directories? # store directories?
buildInputs = dependencies; buildInputs = dependencies;
shell = "${busybox}/bin/sh"; shell = "${busybox}/bin/sh";
inherit up down; # up and down for oneshots are pathnames not scripts
up = writeAshScript "${name}-up" {} up;
down = writeAshScript "${name}-down" {} down;
dependencies = builtins.map (d: d.name) dependencies; dependencies = builtins.map (d: d.name) dependencies;
builder = ./builder.sh; builder = ./builder.sh;
}; };
@ -66,12 +71,14 @@ in {
} // { } // {
inherit device; inherit device;
}; };
address = interface: { family, addr } @ args: oneshot { address = interface: { family, prefixLength, address } @ args:
dependencies = [ interface ]; let inherit (builtins) toString;
name = "${interface.device}.addr.${addr}"; in oneshot {
up = "ip address add ${addr} dev ${interface.device} "; dependencies = [ interface ];
down = "ip address del ${addr} dev ${interface.device} "; name = "${interface.device}.addr.${address}";
}; up = "ip address add ${address}/${toString prefixLength} dev ${interface.device} ";
down = "ip address del ${address}/${toString prefixLength} dev ${interface.device} ";
};
udhcpc = callPackage ./networking/udhcpc.nix {}; udhcpc = callPackage ./networking/udhcpc.nix {};
odhcpc = interface: { ... } @ args: longrun { odhcpc = interface: { ... } @ args: longrun {
name = "${interface.device}.odhcp"; name = "${interface.device}.odhcp";
@ -81,6 +88,6 @@ in {
}; };
services = { services = {
inherit longrun oneshot bundle target; inherit longrun oneshot bundle target;
output = service: name: "/run/s6-rc/scandir/${service.name}/data/outputs/${name}"; output = service: name: "/run/service-state/${service.name}/${name}";
}; };
} }

View File

@ -4,45 +4,40 @@
, busybox , busybox
, ppp , ppp
, pppoe , pppoe
, writeShellScript , writeAshScript
} : } :
let let
inherit (liminix.services) longrun; inherit (liminix.services) longrun;
ip-up = writeShellScript "ip-up" ''
action=$1
env > /run/udhcp.values
set_address() {
ip address replace $ip/$mask dev $interface
mkdir -p data/outputs
for i in lease mask ip router siaddr dns serverid subnet opt53 interface ; do
echo ''${!i} > data/outputs/$i
done
}
case $action in
deconfig)
ip address flush $interface
ip link set up dev $interface
;;
bound)
# this doesn't actually replace, it adds a new address.
set_address
;;
renew)
set_address
;;
nak)
echo "received NAK on $interface"
;;
esac
'';
in in
interface: { interface: {
synchronous ? false synchronous ? false
, ppp-options ? [] , ppp-options ? []
, ... , ...
} @ args: longrun { } @ args:
name = "${interface.device}.ppppoe"; let
run = "${ppp}/bin/pppd pty '${pppoe}/bin/pppoe -I ${interface.device}' ${lib.concatStringsSep " " ppp-options}" ; name = "${interface.device}.pppoe";
ip-up = writeAshScript "ip-up" {} ''
outputs=/run/service-state/${name}.service/
mkdir -p $outputs
(cd $outputs
echo $1 > ifname
echo $2 > tty
echo $3 > speed
echo $4 > address
echo $5 > peer-address
)
echo >/proc/self/fd/10
'';
ppp-options' = ppp-options ++ [
"ip-up-script" ip-up
"ipparam" name
"nodetach"
];
in
longrun {
inherit name;
run = "${ppp}/bin/pppd pty '${pppoe}/bin/pppoe -I ${interface.device}' ${lib.concatStringsSep " " ppp-options'}" ;
notification-fd = 10;
dependencies = [ interface ];
} }

View File

@ -5,15 +5,19 @@
} : } :
let let
inherit (liminix.services) longrun; inherit (liminix.services) longrun;
in
interface: { ... } @ args:
let
name = "${interface.device}.udhcp";
script = writeShellScript "udhcp-notify" '' script = writeShellScript "udhcp-notify" ''
action=$1 action=$1
env > /run/udhcp.values
set_address() { set_address() {
ip address replace $ip/$mask dev $interface ip address replace $ip/$mask dev $interface
mkdir -p data/outputs dir=/run/service-state/${name}.service/
mkdir -p $dir
for i in lease mask ip router siaddr dns serverid subnet opt53 interface ; do for i in lease mask ip router siaddr dns serverid subnet opt53 interface ; do
echo ''${!i} > data/outputs/$i echo ''${!i} > $dir/$i
done done
} }
case $action in case $action in
@ -33,11 +37,10 @@ case $action in
;; ;;
esac esac
''; '';
in longrun {
in inherit name;
interface: { ... } @ args: longrun {
name = "${interface.device}.udhcp";
run = "${busybox}/bin/udhcpc -f -i ${interface.device} -s ${script}"; run = "${busybox}/bin/udhcpc -f -i ${interface.device} -s ${script}";
dependencies = [ interface ];
} }
# lease=86400 # lease=86400

View File

@ -0,0 +1,14 @@
{
busybox
, writeScript
, lib
}
: name : { runtimeInputs ? [] } : text : writeScript name ''
#!${busybox}/bin/sh
set -o errexit
set -o nounset
set -o pipefail
export PATH="${lib.makeBinPath ([ busybox ] ++ runtimeInputs)}:$PATH"
${text}
''

View File

@ -8,39 +8,56 @@ in rec {
in bundle { in bundle {
name = "loopback"; name = "loopback";
contents = [ contents = [
(address iface { family = "inet4"; addr ="127.0.0.1";}) (address iface { family = "inet4"; address ="127.0.0.1"; prefixLength = 8;})
(address iface { family = "inet6"; addr ="::1";}) (address iface { family = "inet6"; address ="::1"; prefixLength = 128;})
]; ];
}; };
kernel.config = { kernel.config = {
"IKCONFIG_PROC" = "y";
"PPP" = "y"; "PPP" = "y";
"PPPOE" = "y"; "PPPOE" = "y";
"PPPOL2TP" = "y"; "PPPOL2TP" = "y";
"PPP_ASYNC" = "y";
"PPP_BSDCOMP" = "y";
"PPP_DEFLATE" = "y";
"PPP_MPPE" = "y";
"PPP_SYNC_TTY" = "y";
};
services.syslogd = longrun {
name = "syslogd";
run = "${pkgs.busybox}/bin/syslogd -n -O /run/syslog";
}; };
services.pppoe = services.pppoe =
let iface = interface { type = "hardware"; device = "eth0"; }; let iface = interface { type = "hardware"; device = "eth0"; };
in pppoe iface {}; in pppoe iface {
ppp-options = [
"debug" "+ipv6" "noauth"
"name" "db123@a.1"
"password" "NotReallyTheSecret"
];
};
services.defaultroute4 = services.defaultroute4 =
let iface = services.pppoe; let iface = services.pppoe;
in oneshot { in oneshot {
name = "defaultroute4"; name = "defaultroute4";
up = '' up = ''
ip route add default gw $(cat ${output iface "address"}) ip route add default via $(cat ${output iface "address"})
echo "1" > /sys/net/ipv4/$(cat ${output iface "ifname"}) echo "1" > /proc/sys/net/ipv4/conf/$(cat ${output iface "ifname"}/forwarding)
''; '';
down = '' down = ''
ip route del default gw $(cat ${output iface "address"}) ip route del default via $(cat ${output iface "address"})
echo "0" > /sys/net/ipv4/$(cat ${output iface "ifname"}) echo "0" > /proc/sys/net/ipv4/conf/$(cat ${output iface "ifname"}/forwarding)
''; '';
dependencies = [iface]; dependencies = [iface];
}; };
services.default = target { services.default = target {
name = "default"; name = "default";
contents = with services; [ loopback defaultroute4 ]; contents = with services; [ loopback defaultroute4 syslogd ];
}; };
systemPackages = [ pkgs.hello ] ; systemPackages = [ pkgs.hello ] ;

View File

@ -8,8 +8,8 @@ in rec {
in bundle { in bundle {
name = "loopback"; name = "loopback";
contents = [ contents = [
(address iface { family = "inet4"; addr ="127.0.0.1";}) (address iface { family = "inet4"; address ="127.0.0.1"; prefixLength = 8;})
(address iface { family = "inet6"; addr ="::1";}) (address iface { family = "inet6"; address ="::1"; prefixLength = 128;})
]; ];
}; };
services.dhcpv4 = services.dhcpv4 =