1
0
forked from dan/liminix

Compare commits

...

595 Commits

Author SHA1 Message Date
Arnout Engelen
a3f6234ef0
WIP: create a FIT image for TFTP booting
TODO: make 'traditional' and FIT methods work side-by-side
2025-01-02 09:46:34 +01:00
2e513eb4a7 example sni proxy using nginx 2024-12-29 23:34:15 +00:00
f2e4e77d73 firewall: don't use oifname in input rules
because it's empty, these are input rules for the local machine
2024-12-29 23:17:31 +00:00
48dfbe0c01 add nginx-small : nginx with finegrained configure options 2024-12-29 20:47:03 +00:00
6f697db57c remove PSTORE from rt3200 default kconfig
we have config.logging.persistent.enable at home
2024-12-29 13:33:55 +00:00
fe1ee12e3d swap strchr for strchrnul in dropbear authkeyfile patch
The strchrnul version was giving weird crashes on aarch64
belkin-rt3200. I haven't figured out why but this one doesn't
2024-12-29 13:30:21 +00:00
4d273a9469 dropbear would like /etc/shells to exist 2024-12-29 13:27:49 +00:00
40db175b41 complain if user attempting to tftpboot a ubifs 2024-12-29 13:26:45 +00:00
ab07212a7e include jffs2 module per default
it has no effect unless enabled
2024-12-29 13:26:06 +00:00
f5e08ac9d9 rt3200 default to loader.fit 2024-12-29 13:25:26 +00:00
0cb18eabcd boot.expect: improve reliability
don't depend on seeing u-boot prompt, it's just too easy to get
out of sync
2024-12-27 18:08:01 +00:00
24151425b8 and fix quoting 2024-12-24 14:29:01 +00:00
e06295ed83 rt3200: add fw_setenv commands to update boot_production 2024-12-24 14:19:29 +00:00
608d3e3abf proofread 2024-12-24 14:01:30 +00:00
3e19f1b927 Merge branch 'runciter' 2024-12-24 13:47:27 +00:00
3f6e9b6384 rt3200 defaults to ubifs 2024-12-24 13:46:36 +00:00
294492a176 jiggle imports 2024-12-24 13:46:19 +00:00
67a1cd3718 improve install instructions for belkin rt3200 2024-12-24 13:45:11 +00:00
Arnout Engelen
f8a275d1a3 use Linux kernel sources associated with openwrt by default 2024-12-24 12:30:15 +00:00
0ee9c76c33 think 2024-12-24 12:30:15 +00:00
dan
452aaa2f60 Merge pull request 'use Linux kernel sources associated with openwrt by default' () from raboof/liminix:linux-version-with-openwrt into main
Reviewed-on: 
2024-12-24 12:24:31 +00:00
dan
52967f746b Merge branch 'main' into linux-version-with-openwrt 2024-12-24 12:24:13 +00:00
Arnout Engelen
a89f866bf0 use Linux kernel sources associated with openwrt by default 2024-12-24 12:21:28 +00:00
f3fadd5cd7 think 2024-12-24 12:20:48 +00:00
bc20f4c6b7 rt3200 test install 2024-12-23 23:59:52 +00:00
848214d104 add ubivolume output 2024-12-23 22:37:07 +00:00
ede8f12d2b declare options.hardware.ubi unconditionally
this is so it can be defined in device modules even when
ubifs is not included in the configuration
2024-12-23 22:37:07 +00:00
6cd5b90678 outputs.rootubifs -> ubifs 2024-12-23 22:37:07 +00:00
db4f098c02 add fit bootloader
this is for the belkin rt3200, whose uboot doesn't do
extlinux but can load a fit from a ubifs. It adds the
a kernel+dtb as /boot/fit
2024-12-23 11:21:58 +00:00
1347937345 rename file 2024-12-23 10:31:22 +00:00
a7b5f80674 rename extlinux output to bootfiles
this is in preparation for introducing other non-extlinux
modules that populate /boot
2024-12-23 00:09:31 +00:00
5c78338d71 make mtdutils use no-systemd util-linux 2024-12-22 23:24:11 +00:00
ed02d02767 bump NEWS 2024-12-22 21:12:36 +00:00
f07a38b0fd extract uimage output module into own file 2024-12-22 21:10:07 +00:00
ac189f2977 outputs.zimage -> outputs.kernel.zImage
remove config option/derivation in favour of accessing
as output of the kernel derivation (matches what we do
with e.g. modulesupport)
2024-12-22 17:27:59 +00:00
ebb4d4a831 think 2024-12-22 16:03:24 +00:00
6bfbdf352d bordervm: expose ssh on port 2222 2024-12-22 16:01:38 +00:00
4ea1cf7f32 rt3200 better install docs 2024-12-20 22:26:40 +00:00
f60b74f415 add a new updater output
this is so that we don't have to obfuscate store paths in
systemConfiguration to avoid dragging in build system
deps.

breaking-ish change to workflows, docs updated
2024-12-20 00:05:07 +00:00
812e35b7b9 systemconfig: improve filenames/pathnames
no more make-stuff
2024-12-19 22:28:30 +00:00
172f368633 fix markup 2024-12-19 21:59:04 +00:00
1af9a39db1 omnia: delete pstore config we're probably not using 2024-12-19 20:59:52 +00:00
420552ce98 add omnia to ci 2024-12-19 20:56:05 +00:00
56c667cfd5 extract systemConfiguration into its own output module 2024-12-19 20:55:10 +00:00
f9b4f0bc9c move modules/squashfs.nix into outputs/ 2024-12-19 14:33:50 +00:00
ba5e4704a0 add short note about persistent logs 2024-12-18 23:08:28 +00:00
3357d21d7f enlarge pmsg buffer to full size of ramoops region
4k was a piddly amount and we weren't using the rest of it for
anything else
2024-12-18 21:16:49 +00:00
ffaca615ba copy logs to /dev/pmsg0 when ogging.persistent.enabled 2024-12-18 21:11:58 +00:00
77cd4492b2 unbreak nix-shell 2024-12-17 23:26:56 +00:00
81f5550bf0 config.logging.persistent enables /dev/pmsg0
- whatever's written to /dev/pmsg0 appears as
/sys/fs/pstore/pmsg-ramoops-0 after reboot

- only works on devices with the relevant device tree
support (gl-ar750 and whatever has it by default)

- nothing in the system is actually writing this file yet

- or reading it at boot time, for that matter
2024-12-17 23:24:31 +00:00
b52133a28b add hardware.dts.includes option 2024-12-17 20:36:14 +00:00
Arnout Engelen
1ff779c1a9
use Linux kernel sources associated with openwrt by default 2024-12-17 20:24:14 +01:00
44caefcd3b rename config.hardware.dts.includes -> includePaths
(1) it's a better name
(2) I want to use `includes` to specify dtsi files
2024-12-17 17:41:53 +00:00
6e6b8790eb think 2024-12-17 17:24:52 +00:00
2e5a8a572e tufted: more robust merge-pathname impl 2024-12-17 17:24:40 +00:00
464d046b5a append-path spec behaviour for repeated / 2024-12-17 17:24:16 +00:00
ac8b971cc0 new fn append-path in anoia
complains if you try to ../../../
2024-12-11 17:26:44 +00:00
13087d17e3 use assert macros in anoia/init.fnl
there is no circularity (maybe there was once?)
2024-12-11 17:25:39 +00:00
5572c0ecb0 rewrite parts of inout test to be differently wrong 2024-12-09 23:36:22 +00:00
4cbe3ba683 add some debug output in inout test 2024-12-09 21:00:11 +00:00
20f4a12689 inout: improve robustness, maybe? 2024-12-07 16:02:42 +00:00
33e5c436d5 add environment variables that scapy now needs (24.11) 2024-12-04 21:16:36 +00:00
cde30bcd54 in nixos 24.11 chrony no longer expects nss/nspr/readline 2024-12-03 21:39:54 +00:00
1f7d6544e3 provide stdout to ppp callback scripts
pppd runs them with 0,1,2 => /dev/null but we actually quite like
seeing errors in the logs
2024-10-17 21:37:08 +01:00
1bca072509 fix chrony pidfile error 2024-10-17 21:35:33 +01:00
7b98724643 turns out we did need usepeerdns 2024-10-17 21:05:16 +01:00
b1625763ee ppp service signal readiness only when ip-up has run
as downstream services need e.g. ifname which is not written by ipv6-up
2024-10-16 22:59:01 +01:00
91bdfc2766 remove apparently obsolete rp-pppoe configure setting
this were copied from nixpkgs but perhaps is for an older version of
rp-pppoe because it builds just fine without
2024-10-16 22:56:05 +01:00
14bfebc5c3 enable unloading modules so that scripts work
if we can't unload them then the service that loads them will fail
the second time it's run
2024-10-16 22:54:19 +01:00
0447ac0ff9 did we need MODULE_SIG?
I think this may be a hangover from using backports modules for wlan
2024-10-16 22:53:16 +01:00
e35a1514ab send kernel logs to s6 2024-10-16 18:59:42 +01:00
4a0120487c remove usepeerdns - it causes only errors
we handle dns with service outputs anyway
2024-10-16 18:58:34 +01:00
888688ce28 buuld ppp with path to /run 2024-10-16 18:57:26 +01:00
9e3f48768e think 2024-10-14 18:49:10 +01:00
72171021e3 support finish script in longrun 2024-10-10 18:26:14 +01:00
17517dd34f remove KEXEC from base kernel config
we're not using it any more
2024-10-10 18:23:50 +01:00
5112eab4da apply incoming-allowed-ip[46] rules to input as well as forward pkts
this makes it possible to open ports on the router itself
2024-10-10 18:18:23 +01:00
e383f1b3d3 obfuscate store path for min-copy-closure
otherwise the systemconfig closure drags in a bunch of build system
things (bash, etc) which we don't want or need to copy to the device
2024-10-10 16:25:00 +01:00
da1245432e no more iminix-rebuild 2024-10-09 19:34:55 +01:00
541b1c61c2 ensure $toplevel is path in /nix/store 2024-10-09 18:59:33 +01:00
55c7410a55 add result/install.sh to systemConfiguration output
this makes it possible to install a systemconfig instead of
having to use nix-shell (which is very slow)
2024-10-09 13:35:02 +01:00
0f50648157 don't put hostname in levitate logs
there might not be one
2024-10-08 22:55:39 +01:00
f1c260d4f7 make ci.ni "all" a derivation
this is to stop hydra complaining
2024-10-06 18:04:56 +01:00
3d611d3ba2 fix unstable qemu build?
nix-repl> (lib.versionOlder "24.11pre-git" "24.11")
true

nix-repl> (lib.versionOlder "24.11pre-git" "24.10")
false

n
2024-10-06 18:04:48 +01:00
e6b7d86381 sort lines 2024-10-06 17:53:34 +01:00
83fbffb39b catch another uncaught-logs 2024-10-06 17:53:09 +01:00
f8c579b41e add CI "all" target 2024-10-06 17:52:59 +01:00
ca9efc4b26 simplify CI
* I didn't know what I was doing when I set up Hydra

* it's not certain that I do now either, but hey ho
2024-10-06 15:55:01 +01:00
336fc7e495 think 2024-10-06 14:27:45 +01:00
4cc0add2ad update refs to uncaught-logs in docs/tests 2024-10-06 13:46:14 +01:00
2d7e6188ac log shipping service now gets logs on stdin
instead of having to open the unix socket
2024-10-06 13:26:58 +01:00
b9999857cb longrun: don't add logger if producer-for is already set 2024-10-06 13:13:04 +01:00
ba03ddeb38 border-vm: add tang service 2024-10-06 12:38:06 +01:00
493c5f69d7 add module for certifix-client 2024-10-06 11:27:39 +01:00
1a915e91ff add altname to CSR 2024-10-06 10:13:28 +01:00
197e2eb5b1 new package certifix-client uses certifix to sign ssl client cert
this is initially for TLS-enabled logging but would be useful for
anything on a liminix box that wants to talk to a network service in a
"zero trust" setup
2024-10-03 23:00:08 +01:00
7ca822c826 more messing around with lua derivation 2024-10-03 23:00:08 +01:00
e5631783e1 add luaossl package with patch for CSR attributes 2024-10-03 23:00:08 +01:00
635590d37a implement log shipping config
to use this, you need config like for example

+  logging.shipping = {
+    enable = true;
+    service = longrun {
+      name = "ship-logs";
+      run = let path = lib.makeBinPath (with pkgs; [ s6 s6-networking s6 execline ]);
+            in ''
+        PATH=${path}:$PATH
+        s6-ipcserver -1 ${config.logging.shipping.socket} \
+        s6-tcpclient 10.0.2.2 19612 \
+        fdmove -c 1 7 cat
+      '';
+    };
+  };

but I think we can reduce the noise a bit if we use an s6-rc pipeline
with an s6-ipcserver on one side and and a (whatever the user wants)
on the other
2024-09-18 22:14:34 +01:00
17630f2678 rename logtee->logtap 2024-09-18 20:58:02 +01:00
707a471bc2 add logtee to catchall logger 2024-09-16 21:30:06 +01:00
d3fce5edd4 implement error() for musl 2024-09-16 20:35:23 +01:00
5771108fed improve logtee socket connection warning
* print it less often
* to the correct stream (stdout not stderr)
2024-09-16 20:34:26 +01:00
9e5f2d663d close socket fd if we can't connect it 2024-09-15 22:09:31 +01:00
21eeb1671e print diagnostic when eof on stderr 2024-09-15 21:59:24 +01:00
44762d38fc write start cookie when socket connect succeeds 2024-09-15 21:54:21 +01:00
1f6cfc3679 extract method is_connected 2024-09-15 21:40:05 +01:00
8ec00f1710 improve error message 2024-09-15 21:37:04 +01:00
6a6dd32dea make pollfd array global 2024-09-15 21:32:48 +01:00
9b1fc11a59 logshipper/logtee :copy stdin to stdout & to a unix socket if present
first draft
2024-09-15 19:33:21 +01:00
aaa6e353db incz is a very rudimentary log shipper for zinc search
although it probably would work with elasticsearch as well
as zinc is alleged to be ES-compatible

this is just the package and needs hooking into the service/log
infrastructure somehow
2024-09-08 16:38:37 +01:00
69bf6cb5fb write-fennel quote PATH properly
escapeShellArg only quotes if the string contains special
characters, but for a Lua string we must quote unconditionally
2024-09-07 22:31:44 +01:00
9f58e7b926 maybe fix nixpkgs-unstable lua 2024-09-07 00:58:11 +01:00
5a5c27ab9f think 2024-09-06 22:37:49 +01:00
277c91acdf Revert "remove luaposix ref in write-fennel"
This reverts commit a60c2539a6.
2024-09-06 00:33:30 +01:00
e0725489ca unbreak pppoe ci job 2024-09-06 00:33:30 +01:00
cc47515cf8 watch-outputs remove debug code 2024-09-06 00:13:54 +01:00
464913cc8f tangc use spawn to invoke jose
hopefully we are now deadlock-free
2024-09-06 00:12:45 +01:00
e604d628e3 fennel anoia.process.spawn
runs a subprocess and invokes a callback whenever its io
descriptors are ready
2024-09-06 00:11:33 +01:00
e2a597589b anoia.fs.find-executable looks for bin in colon-sep list of directories 2024-09-06 00:08:40 +01:00
Raito Bezarius
a139a262c1 seedrng: init at 2022.04
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-09-05 14:18:00 +01:00
6a5fed83dd conditional fetch in json-to-fstree 2024-09-05 11:14:47 +01:00
bcf5ab24e8 tidy watch-outputs startup message 2024-09-05 10:11:16 +01:00
32bf80c6fa devout: unlink socket pathname before binding 2024-09-05 10:05:13 +01:00
12275f6896 add more test for table= 2024-09-04 21:21:30 +01:00
a60c2539a6 remove luaposix ref in write-fennel 2024-09-04 21:21:02 +01:00
146a2d9ac0 fix startup race/fencepost in watch-ssh-keys
if it starts _after_ the outputs are populated, it should
write the first lot of outputs without waiting for a change
2024-09-04 21:19:51 +01:00
091d863710 extract pppoe/l2tp common code 2024-09-04 12:02:00 +01:00
c7bcfbfa34 make pppoe/l2tp more consistent 2024-09-03 22:57:45 +01:00
500a3c1025 make nodefaultroute explicit in ppp 2024-09-03 22:53:13 +01:00
0c0d0eed8a make watch-ssh-keys robust against missing key 2024-09-03 22:51:29 +01:00
699cf97206 improve tangc http error messages 2024-09-03 22:50:55 +01:00
cd0093279c think 2024-09-01 10:14:31 +01:00
034d6aacc4 tangc handle non-zero exit from jwe dec
Sometimes it exits non-zero but decrypts the file *anyway*. It only
does this on the device and I haven't been able to reproduce on build,
so this is a workaround until we find the root cause
2024-09-01 09:57:38 +01:00
e590c0ad3f secrets subscriber: add provider as dep to controlled service 2024-09-01 09:56:59 +01:00
14abdd9998 tang: notify on ready 2024-08-31 23:24:50 +01:00
6287b92000 fix bugs handling base64 padding 2024-08-31 22:43:25 +01:00
d2215d3e56 tangc popen retry on short read 2024-08-31 22:18:23 +01:00
3cf2308bee tangc: stop printing unexpected blank lines 2024-08-31 15:29:10 +01:00
3913989be3 provide string to perform-encryption
instead of letting it read stdin, which I think may have been read
by a subprocess already sometimes?
2024-08-31 15:27:54 +01:00
43e5e6876e improve tangc error messages 2024-08-31 15:22:26 +01:00
7d6c80570c refactor all writeFennelScript calls to use writeFennel directly 2024-08-30 20:57:42 +01:00
e745991b9d restart pppoe/l2tp in secrets changes 2024-08-30 20:49:27 +01:00
defbfce1fb finish converting outputRef to lambda 2024-08-30 20:46:48 +01:00
0df2c83382 tighten perms on service state directory 2024-08-29 23:56:43 +01:00
01c28de88d think 2024-08-29 23:56:20 +01:00
2bf197cad8 document outputs and secrets 2024-08-29 23:55:32 +01:00
a8a19977ca (untested) template service for tang encrypted secrets 2024-08-28 22:32:26 +01:00
8a9284af1e think 2024-08-28 22:23:00 +01:00
7351e143c5 remove redundant sourcing of ${serviceFns}
this is done by the oneshot and longrun functions
2024-08-28 21:28:27 +01:00
283c3154a7 missing file in s6-rc-up-tree test fixture 2024-08-28 21:18:54 +01:00
34f37d60d9 missed adding this 2024-08-28 20:56:52 +01:00
fe7b092075 (untested) http basic auth for outboard secrets 2024-08-28 20:53:59 +01:00
b56f121e04 fetch lua glue: handle missing content-length 2024-08-28 19:52:00 +01:00
d5d621f310 rename http-fstree => json-to-fstree
it works for file urls as well, not just http
2024-08-28 16:36:49 +01:00
da95a9fa62 tangc support encryption 2024-08-28 18:55:20 +01:00
85071c88e7 remove argv0 from calls to jose 2024-08-28 11:16:43 +01:00
74093b7ee3 josep! runs jose without json parsing the output 2024-08-28 08:13:50 +01:00
41733e58d6 remove unused code, tidy string parsing 2024-08-28 07:20:07 +01:00
9041d5d63a add jose! fn to reduce error-checking boilerplate 2024-08-28 07:10:47 +01:00
001ebdc601 remove unused requires 2024-08-28 06:52:04 +01:00
1f97409474 add popen2 to anoia.fs 2024-08-28 06:49:43 +01:00
a41839f3d1 clevis-decrypt-tang in fennel
needs a lot of tidying up, but works on my test file
2024-08-28 01:37:44 +01:00
ff76d854fc extend libfetch lua glue to other HTTP methods 2024-08-28 01:37:02 +01:00
81a6480a4f anoia add base64 deode 2024-08-27 22:42:03 +01:00
c7164a6f4a sshd can use outputRef for authorized_keys 2024-08-25 16:35:50 +01:00
83ca86fe42 keys in service output tree are strings 2024-08-25 15:59:24 +01:00
1b4106e2a3 ssh-keys service, draft 2024-08-25 15:09:31 +01:00
89912c766b nixpkgs 24.11 qemu does not expect texinfo 2024-08-25 14:23:29 +01:00
9828b007ae watch-ssh-keys turns secrets-service into authorized_keys files 2024-08-24 23:25:32 +01:00
f34abc85ae add macros param to write-fennel 2024-08-24 23:19:46 +01:00
b475a680fb define-tests macro, evals body only when inside fennelrepl --test 2024-08-24 22:26:25 +01:00
43612af71a anoia: %% is alias for string.formt 2024-08-24 13:56:54 +01:00
5695c47496 add dig to anoia 2024-08-23 23:27:29 +01:00
e3ec514710 think 2024-08-23 23:27:17 +01:00
99f68e5421 destructure params in ssh service 2024-08-23 23:13:49 +01:00
9c30b6f882 change output references from attrset to lambda
this is so that we can distinguish a ref from a literal parameter that
might be a attrset
2024-08-23 22:25:57 +01:00
dd75322c10 think 2024-08-23 21:45:18 +01:00
869a508c0a add authorizedKeys option to ssh service
this has no apparent use as it stands, but opens the door to
having the keys managed by an external secrets service
2024-08-23 20:35:07 +01:00
e835473945 patch dropbear to add -U option 2024-08-23 19:58:05 +01:00
055268d5d2 upgrade dropbear 2024-08-23 19:57:10 +01:00
ff38bcacbb improve devout error reporting 2024-08-21 23:24:13 +01:00
a6128955e7 ppp modules: permit (mostly) same params for l2tp as pppoe
this also means that l2tp can use secrets for username/password
2024-08-21 23:10:28 +01:00
531cb113be devout needs a longer startup timeout
seems to be taking around 40 seconds now, would be worth digging in to
find out why
2024-08-21 23:09:11 +01:00
daede666cb in router-with-l2tp use secrets for ppp username/password 2024-08-21 00:17:53 +01:00
2992771c7e pppoe allow secrets for username/password 2024-08-21 00:17:22 +01:00
4cc82e1502 liminix.types.replacable is a string or ref to an output 2024-08-21 00:16:14 +01:00
21f2320d86 inline method 2024-08-20 23:26:11 +01:00
d40ada4251 use structured ppp params in ppp test 2024-08-20 23:25:31 +01:00
4053ea9481 secrets/subscriber implement different restart types 2024-08-20 22:56:26 +01:00
54d3415885 pppoe convert to using a config file
mostly for ease of implementation but does mean we don't
have username/password secrets on the command line
2024-08-20 22:55:30 +01:00
264d83c98d move some secret-watching stuff from hostapd to secrets 2024-08-20 21:49:11 +01:00
97defc2076 hostapd: get secrets service/path from attrs 2024-08-17 22:25:30 +01:00
ddaa5476d3 override clevis derivation (experimental) 2024-08-15 23:02:54 +01:00
bcd9d56624 start devout after mdevd
not 100% sure that there's a dependency but it's plausible, and
would explain the observed occasional failure to start at boot
2024-08-15 23:01:29 +01:00
e2c883356c add secrets-subscriber service, make hostapd use it 2024-08-15 23:00:41 +01:00
d79a941504 new package watch-outputs and example of its use 2024-08-14 22:58:17 +01:00
2f82e0dab8 hostapd set permissions on dir in /run/ 2024-08-14 22:57:02 +01:00
fc03965915 hostapd literal_or_output use an attrset for dispatch 2024-08-14 22:56:01 +01:00
d2d3af2587 outboard secrets: loop in service
if we just quit and expect s6 to restart us, the finish script
wipes our outputs and anything with an inotify watch gets confused
2024-08-14 22:41:56 +01:00
310ac30f24 http-fstree needs to write state and .lock for anoia.svc 2024-08-14 22:39:41 +01:00
45a7f96bd4 anoia table= compares tables 2024-08-14 22:36:28 +01:00
79445fd962 support multi-arg assoc 2024-08-14 22:34:37 +01:00
a9ddd78482 think 2024-08-12 22:59:03 +01:00
4fb8253e57 first pass at outboard secrets
- a module to fetch them with http(s)
- a service using templating to consume them
- update an example to use it

needs service restarts
needs other services to use the template mechanism
needs tidying up
2024-08-12 22:57:21 +01:00
ff3a1905a5 pass service to output fn in output-template
instead of on command line
2024-08-12 22:53:07 +01:00
3c353e4aff support json quoting in output-template 2024-08-10 23:42:08 +01:00
ba21384fde new: output-template interpolates output values into config file 2024-08-10 23:06:47 +01:00
2480fdef5b set up nginx on bordervm for testing outboard secrets 2024-08-10 23:05:50 +01:00
409c1cfb16 think 2024-08-10 23:05:15 +01:00
9767078878 add the example used in the video 2024-08-08 19:24:58 +01:00
d760c2d27b http-fstree downloads a json file and converts to service outputs 2024-08-08 15:35:11 +01:00
1e139c22fd think 2024-08-08 15:21:24 +01:00
a1ff07b063 add rxi/json lua module 2024-08-08 15:05:26 +01:00
9550772cec add lua binding to fetch-freebsd 2024-08-08 15:05:03 +01:00
64cd1626c6 new package fetch-freebsd: small http(s) client library
[*] smaller than curl, maybe not maximally small
2024-08-08 11:38:38 +01:00
eb79928b37 anoia.svc allow writing outputs 2024-08-08 11:37:50 +01:00
0a629df48d anoia.fs: improve error messages 2024-08-08 11:36:47 +01:00
64afd18e2a why does this fail on hydra? 2024-08-06 23:18:39 +01:00
47e96ddc15 think 2024-08-06 18:43:49 +01:00
5db9d7269e ppoe structured options are optional 2024-08-06 18:43:27 +01:00
985df8792d overlay: handle cross-only overrides consistently 2024-08-06 18:42:58 +01:00
528afae8b1 doc: punctuate 2024-08-06 14:15:57 +01:00
384835c89d admin doc: updte round-robin, explain health check 2024-08-06 14:14:52 +01:00
5051625d31 mention health check in docs 2024-07-30 22:53:21 +01:00
c4d00e062a add health check service and example that uses it 2024-07-30 22:37:43 +01:00
8fa3443923 Revert "anoia.svc use timeout for inotify"
This reverts commit eca8e37e7a.
2024-07-30 17:37:38 +01:00
8091e207b6 some notes on controlled services 2024-07-28 22:57:23 +01:00
39020607ad rename service-trigger rule to match service name 2024-07-28 22:35:37 +01:00
fe735408a1 v:address is nil if missing, but code expects an array 2024-07-27 17:40:32 +01:00
a9d1582b53 remove unused arg 2024-07-26 23:41:50 +01:00
eca8e37e7a anoia.svc use timeout for inotify
in case we miss a message, check the directory every 5s
anyway
2024-07-26 23:40:40 +01:00
d300373b96 anoia fs.dir use case not match
match was accidentally pinning the return from readdir against the
function parameter. Which didn't work.
2024-07-26 23:37:40 +01:00
70ca7fac17 elfutils is reqd by iproute2 (for bpf?), build sans kitchen sink 2024-07-24 22:07:58 +01:00
79a3a45061 build iproute2 without rb to avoid stdatomic 2024-07-24 21:13:55 +01:00
612d6d7a51 build openssl without threads to avoid stdatomic 2024-07-24 21:12:52 +01:00
e1ae986cf6 convert l2tp example to use gateway profile 2024-07-23 09:31:34 +01:00
bce0c7ffb6 rename services.dhcpc in l2tp example
it's only used to get the address of the l2tp server, not for
name lookups in general
2024-07-23 09:31:34 +01:00
28ca1e68ab wwan module needs mdevd 2024-07-23 09:31:34 +01:00
acf33a100f think 2024-07-23 09:31:34 +01:00
7f9cae9d5c generalise profile.gateway.wan so not just pppoe 2024-07-23 09:31:34 +01:00
3012c91b47 executive decision: rotuer example should build on gl-ar750 2024-07-23 09:31:34 +01:00
1edf20c08f fix whitespace 2024-07-23 09:31:34 +01:00
7195cb10ce add structured config for common pppoe options 2024-07-23 09:31:34 +01:00
135a445672 restore param removed by deadnix
dochain is called with `family` even if it never uses it
2024-07-16 20:41:21 +01:00
3899daee56 create a module for round-robin 2024-07-15 22:37:37 +01:00
b17f623d03 need insmod when we habve kmodloader 2024-07-15 22:35:26 +01:00
df395a4d5d finish moving pkgs.linimix.callService to config.system 2024-07-15 19:00:08 +01:00
75e9f8210c remove the fixpoint we didn't need 2024-07-15 18:54:04 +01:00
1c3242cab1 doc: swap order of configuration and installation
you can get a device up and running using a lightly edited example
config before you need to read all the reference info, so let's
have the documentation in that order.
2024-07-14 12:26:07 +01:00
44ea683391 think 2024-07-14 12:08:02 +01:00
725d8b608f huawei-cdc-ncm kernel driver -> module 2024-07-14 12:07:28 +01:00
bc9ced5d38 fix doc ref from admin section -> configuration 2024-07-14 11:56:35 +01:00
73ae7788b9 rename wwan-related modules/services
we only currently support huawei e3372/cdc ncm so let's make that
explicit in the naming
2024-07-14 11:53:45 +01:00
d34919766a improve reinstallation docs 2024-07-12 18:38:04 +01:00
2fe0cd2f48 add first draft instructions for using Levitate 2024-07-12 00:17:25 +01:00
241f1013ed add new Installation guide
move the u-boot/serial stuff here from development, as the
reality of Liminix development in 2024 is that serial connection
is still the smoothest installation method
2024-07-11 23:31:00 +01:00
2ce361d4e3 think 2024-07-11 09:39:38 +01:00
3f8cc24dcc fix most doc warnings 2024-07-10 23:36:24 +01:00
57e3b449f8 proofreading 2024-07-10 21:23:24 +01:00
3964505131 some notes on services 2024-07-10 20:50:08 +01:00
941479b144 use round-robin failiover in l2tp example 2024-07-08 22:01:54 +01:00
ac551536da set cwd before exec xl2tpd 2024-07-08 21:56:26 +01:00
6f908156af fix dependency between modem-atz and modeswitch
for values of "fix" more than slightly reminiscent of "kludge"
2024-07-08 21:55:05 +01:00
534a49e827 s6-rc-round-robin
runs services in order, starting the next one when the previous one
dies or fails to start
2024-07-08 21:53:51 +01:00
07a6eb73cd set lcp-echo timeout in l2tp 2024-07-08 21:45:54 +01:00
159bfa3057 make xl2tpd quit when the connections close 2024-07-08 21:44:15 +01:00
8f0ab5be40 enable tail -F 2024-07-08 21:37:07 +01:00
7f9971512d a6-rc-up-tree: handle blocked deps, exit 1 if nothing started 2024-07-08 21:28:31 +01:00
f0f6cc80d7 remove dead code 2024-07-08 21:28:11 +01:00
afcc6a6436 s6-rc-up-tree pass -b to s6-rc command 2024-07-08 21:27:54 +01:00
2e8e05f31a wip: rewrite s6-rc-up-tree in an actual procgramming language
and write some tests for it, too
2024-07-08 21:27:42 +01:00
143137cbc6 pppoe: set lcp echo failure timeout 2024-07-08 21:25:42 +01:00
8d228f2bef mess with redial 2024-07-08 21:24:44 +01:00
5751058d59 gl-ar750 swap lan and wan
I don't know if I just got it wrong the first time or if something
weird is going on
2024-07-08 21:19:30 +01:00
5ac7e1e9b2 write-fennel: set $PATH if lualinux is available 2024-07-08 21:18:02 +01:00
c75452549b think 2024-07-08 21:17:12 +01:00
2663f58807 disable security for bordervm "liminix" share
tftp needs to be able to follow symlinks into the store
2024-07-01 20:53:03 +01:00
9dbc285605 build libusb1 without libatomic 2024-06-30 17:52:17 +01:00
8b6aa2134e zyxel dual image; restore deleted params 2024-06-30 17:50:45 +01:00
3df1ec76ff cleanup whitespace and commas
* [] is now [ ]
* {} is now { }
* commas in arglists go at end of line not beginning

In short, I ran the whole thing through nixfmt-rfc-style but only
accepted about 30% of its changes. I might grow accustomed to more
of it over time
2024-06-30 17:16:28 +01:00
0d3218127f remove unused makeWrapper input 2024-06-30 10:46:37 +01:00
e94bf62ec1 remove dead code (run deadnix) 2024-06-29 22:59:27 +01:00
16a2499d74 avoid makeWrapper on host, it requires bash 2024-06-29 22:36:05 +01:00
d4d8093f97 working l2tp-over-wwan stick example 2024-06-20 10:15:54 +01:00
7c9c801afc rename isTrigger to restart-on-upgrade
we're moving away from "trigger" services to "controller" services,
and "restart-on-upgrade" is the name used by s6-rc
2024-06-16 12:58:06 +01:00
c4185617c0 a6-rc-up-tree wait for lock if needed 2024-06-15 15:36:07 +01:00
06d28e9b08 dhcpc handle case when env vars are missing
the notify-script should continue and signal readiness even if one or
more of the outputs it writes are mssing in the environment
2024-06-15 15:34:49 +01:00
9540fc2641 add writeAshScriptBin (forgot to add file) 2024-06-15 15:04:56 +01:00
adc84108ad Revert "wwan gets address from ppp ipcp not dhcp"
This reverts commit be13ab23ca.
2024-06-15 15:04:33 +01:00
eae99051fa exec devout in service definition
makes little practical difference but saves a process slot
2024-06-15 15:01:57 +01:00
49d1703428 add s6-rc-up-tree: start reverse deps of controlled service
When s6-rc stops a service, it also stops everything that
depends on it. but when it starts a service it starts only
that service, so we have to go through the other services
depending on it and figure out if they should be started too.
2024-06-15 14:59:34 +01:00
1d337588f9 think 2024-06-15 09:04:19 +01:00
29a869b4fa qemu: use kmodloader for wifi 2024-06-13 10:12:17 +01:00
5ae1b0a193 Revert "bodervm: remove usbutils until we can fix the udev dep"
This reverts commit c22e3fb2ef.
2024-06-12 20:58:13 +01:00
473a4947a5 inout test: wait longer for disk to appear 2024-06-12 20:44:03 +01:00
50bad5c604 libusb needs udev on build
this is a workaround to make CI work again, but what we really need to
do is completely separate the nixpkgs used for nixos build-system
tools from the nixpkgs we use for liminix host binaries
2024-06-12 18:55:30 +01:00
c22e3fb2ef bodervm: remove usbutils until we can fix the udev dep 2024-06-12 13:07:29 +01:00
f898e4dca2 remove debug 2024-06-12 13:03:26 +01:00
5121a8563d callService: dependencies are services not names 2024-06-12 12:58:57 +01:00
78be354b6e think 2024-06-12 12:52:52 +01:00
be13ab23ca wwan gets address from ppp ipcp not dhcp 2024-06-12 12:51:07 +01:00
4b30cd7a75 think 2024-06-11 14:05:32 +01:00
b15542b668 start correct services at boot
- uncontrolled services that are not dependent on a controlled service
- controllers
- _not_ controlled services or any other service that depends on one
2024-06-11 14:04:14 +01:00
6daeaf29a0 flip controller/controlled relationship for wwan services 2024-06-11 14:02:48 +01:00
e6ca5ea064 store derivations not just names for service deps
.. also controllers, contents. This is to make it possible (easier)
to work out transitive dependencies at build time
2024-06-11 14:01:06 +01:00
e6e4665a18 flip dependencies for triggered/controlled services
Instead of treating the trigger as the "main" service and the
triggered service as subsidary, now we treat the triggered
service as the service and the trigger as "subsidary". This
needs some special handling when we work out which services
go in the default bundle, but it works better for declaring
dependencies on triggered services because it means the
dependency runs after the triggered service comes up, not
just when the watcher-for-events starts
2024-06-09 22:37:45 +01:00
2c10790a6d think 2024-06-09 11:19:38 +01:00
571adf84c0 inherit builtins.map 2024-06-07 16:55:45 +01:00
c8c79fd75a update all calls to uevent-watch 2024-06-02 20:42:09 +01:00
884d8d194e wrap uevent-watch in a service 2024-06-02 20:42:09 +01:00
f091bbd706 devout: recognise attr,attrs when parsing search term string 2024-06-01 23:48:05 +01:00
37d7e20582 wwan use uevent-watch to find tty for AT commands 2024-06-01 23:47:20 +01:00
04b068f7a3 delete unused code 2024-06-01 22:43:48 +01:00
53f57c1a8c devout: support sysfs attributes for (grand*)parent device 2024-06-01 22:43:27 +01:00
19aba0d873 devout: support search for sysfs attributes 2024-06-01 21:20:41 +01:00
7d00b39249 rename attributes->properties when referring to uevent fields
properties: key-value pairs in the uevent message
attributes: file contents in sysfs
2024-06-01 12:17:49 +01:00
7aa8633cde think 2024-06-01 12:16:21 +01:00
58bec8a40f semi-automate tftpbooting with minicom 2024-05-26 18:03:32 +01:00
a3fca5bf05 devout: add functions to read sysfs attributes 2024-05-26 18:03:32 +01:00
e0bd7aec1e wwan: hook usb-modeswitch to uevent 2024-05-26 18:03:32 +01:00
e815f61bb5 think 2024-05-26 18:00:31 +01:00
af9200a136 skip symlink handing unless linkname was provided 2024-05-26 18:00:31 +01:00
898958fa10 make a serviceDefn for wwan 2024-05-22 18:54:49 +01:00
fa0f262706 commentary 2024-05-22 18:54:49 +01:00
71aeb27b2f add hacky wwan service with hardcoding all over 2024-05-22 18:54:49 +01:00
530b4080c9 create cdc-ncm module 2024-05-22 18:54:49 +01:00
58cd007ccc barebones usb_modeswitch package 2024-05-22 18:54:49 +01:00
3a56798eb5 l2tp set default route via tunnel 2024-05-22 18:54:49 +01:00
758c7ef657 exec xl2tpd
haven't fully worked out why, but without this s6 is unable to stop it.
2024-05-22 18:54:49 +01:00
73225a70b2 add rudimentary l2tp service module 2024-05-22 18:54:49 +01:00
ab304dd3f1 bordervm enable nat 2024-05-22 18:47:37 +01:00
0d49f0f7a7 gl-ar750 appendDTB 2024-05-22 18:47:16 +01:00
e64390460a memorable net device names for gl-ar750
linux's view of eth1 and eth0 are opposite to that of u-boot
2024-05-22 18:47:08 +01:00
c0ef6ce282 list pkgs we need in bordervm build
it's a bit silly trying to build it with the whole liminix overlay
when it's a nixos system not a liminix system
2024-05-22 18:45:35 +01:00
bd6ec5201f run dhcp server on bordervm
this is for testing clients that have dhcp upstream
2024-05-22 18:45:35 +01:00
b4068da9fe tftp addresses 2024-05-22 18:45:35 +01:00
aa4b09da85 think (foreshadowing) 2024-05-22 18:45:23 +01:00
471c63b399 s6-rc do cleanup in "finish", don't append to "run" script
s6-supervise sends signals (e.g. SIGTERM) to the pid of the process
running "run", so how do we know if the ceanup commands are even
getting executed if the shell interpreter that is supposed to do that
got killed already?
2024-05-13 17:53:02 +01:00
782feaeafa set default for firewall extraRules 2024-05-03 16:28:53 +01:00
ac54c89427 add busybox to bordervm for udhcpd 2024-05-01 23:09:23 +01:00
5a3646cb29 add authorized keys to bordervm
You don't often need this because it has autologin, but sometimes
you want to do antics involving sshing through it to the wan port
of a test device.

Note that you probably wanted to start bordervm with funny qemu
options to even make that possible

 nix-shell --run "QEMU_NET_OPTS=hostfwd=tcp::10022-:22 run-border-vm"
2024-05-01 23:07:11 +01:00
e249f48cff add deps on {ins,rm}mod and kconfig for firewall module 2024-05-01 23:06:12 +01:00
6661e42684 mt300a tftpboot needs appendDTB 2024-05-01 23:04:25 +01:00
b9ba9ef835 mt300a remove unneeded service dependencies 2024-05-01 23:03:55 +01:00
8b69dcc209 pass entire config fragment to levitate, not just services
to make it useful we need to be able to set packages, passwords, ssh
keys etc
2024-04-29 20:07:01 +01:00
9b3a3b9ff7 add levitate to arhcive
this is largely untested
2024-04-28 21:38:13 +01:00
7d08497bcb arhcive remove coldplug fudge 2024-04-28 21:37:30 +01:00
0e84adaa0e maybe don't need deps for gl-mt300a vlan devices?
will delete them next time I have that device open to test
2024-04-28 21:35:09 +01:00
660ed5df8f vlan interface services depend on primary 2024-04-28 21:33:36 +01:00
792a11c8c0 gl-mt300n-v2 use full path to swconfig in service stop 2024-04-28 21:32:42 +01:00
7e4a05bbf8 separate kernel and base modules
this is needed for levitate
2024-04-28 12:44:27 +01:00
a4ba5c85e1 alphabetize list in all-modules 2024-04-28 12:42:47 +01:00
723ef73d5a inout: test hotplug and coldplug 2024-04-27 22:41:30 +01:00
3d4e782929 devout: run tests in postBuild
because checkPhase is not executed when cross-compiling, and this
package is always only cross-compiled
2024-04-27 21:07:25 +01:00
1b6a05aec5 make uevent-watch use devout instead of direct netlink 2024-04-27 21:07:25 +01:00
80628a3d90 move event matching tests to devout
in preparation for future uevent-watch not needing to do
event matching
2024-04-27 21:07:25 +01:00
bf0cafffed start devout alongside mdevd
ensure it starts before mdevd-coldplug so it can populate
its database
2024-04-26 20:52:12 +01:00
e49aba127c devout: improve socket error handling 2024-04-26 20:49:23 +01:00
324465bc18 devout: write uevent KEY=value format to clients 2024-04-26 17:37:28 +01:00
b33249a050 devout: add readiness notification 2024-04-26 17:23:29 +01:00
b9c084415e devout: handle readiness on netlink socket but no event 2024-04-26 17:20:33 +01:00
cf9cadd212 devout: replay relevant events to new subscriber 2024-04-26 17:20:33 +01:00
a116fe084a devout: use socket constants from anoia.net.constants 2024-04-26 16:48:51 +01:00
74cf3e0711 add anoia.net.constants for SOCK_{STREAM,DGRAM} etc
we use an ugly bit of C preprocessor to get the values from
header files, because certain constants are different on MIPS
than on other architectures
2024-04-26 16:43:09 +01:00
9795f03da4 think 2024-04-26 16:41:31 +01:00
cdb23b147c convert anoia.fs to use lualinux 2024-04-25 21:14:37 +01:00
dbd1264352 convert anoia.fs to use lualinux instead of lfs 2024-04-24 20:44:32 +01:00
834858d5bc think 2024-04-24 18:33:57 +01:00
18335b95e3 devout: strip newlines from client terms
this is just to make testing with socat easier
2024-04-24 18:33:02 +01:00
6bee2f67ac devout: add incoming netlink messages to database 2024-04-24 18:32:27 +01:00
b4ba3eea21 fix revents in unpack-pollfds 2024-04-24 18:31:26 +01:00
16af3984c9 add lualinux to fennelrepl 2024-04-24 18:30:34 +01:00
ce7e395295 devout test: replace minisock with lualinux 2024-04-24 18:29:24 +01:00
7e13e017eb add readline suport to fennelrepl 2024-04-24 18:28:39 +01:00
bbf2f53c0e cross-compile lualinux 2024-04-24 18:28:14 +01:00
032d0f8aca add netlink socket
it's not hooked up to anything yet, but it proves we can
do this with lualinux
2024-04-23 23:34:25 +01:00
b8ac9e5279 convert devout from minisock to lualinux 2024-04-23 23:33:11 +01:00
ff2604ca5d think 2024-04-23 23:30:50 +01:00
72789984ce add lualinux package 2024-04-23 22:41:38 +01:00
90d9d0e811 update minisock to not scribble on lua strings 2024-04-23 20:19:33 +01:00
97a8ae1c84 devout: add event loop and main run function 2024-04-23 20:15:02 +01:00
52eb283a26 implement unsubscribe
and add ids to subscribe so that there's a unique identifier
to pass to unsubscribe
2024-04-23 20:12:46 +01:00
cbb1de804e switch to minisock fork witj poll() call
this is likely to be temporary as minisock is getting
replaced with lualinux
2024-04-23 20:09:41 +01:00
f9c03998b8 implement subscriptions with callback 2024-04-21 13:19:17 +01:00
50de1b090f add the rest of the test list (all we've thought of) 2024-04-21 11:22:26 +01:00
648382f64a report bodyless tests as PENDING 2024-04-21 11:19:42 +01:00
e9370358ae implement "remove" events 2024-04-21 11:19:06 +01:00
762ce7b6b8 cut/paste devout implementation into a real module 2024-04-20 22:48:00 +01:00
b1c0560f4f implement fetch by path 2024-04-20 22:20:43 +01:00
e34135c41a improve failed test reporting 2024-04-20 21:46:37 +01:00
712c9b266f implement find 2024-04-20 18:42:42 +01:00
4df963996c devout: add device 2024-04-20 18:24:10 +01:00
349bfecbb8 new package "devout", does nothing yet 2024-04-20 17:45:40 +01:00
450d3820b2 clean up uevent-watch test using writeFennel and mainFunction
requires less cavorting with globals and stuff
2024-04-20 16:53:43 +01:00
771585546d import expect= where previously it was copy-pasted 2024-04-20 15:09:50 +01:00
73abf952d5 package minisock, a minimal Lua socket library 2024-04-20 15:09:17 +01:00
8af4e9fd5b package anoia assert macros and point fennelrepl at them 2024-04-20 14:59:14 +01:00
7e19d80130 anoia: add assert macro module
contains expect and expect=
2024-04-20 14:04:32 +01:00
0f0688c802 think 2024-04-20 14:03:48 +01:00
b43f17f655 think 2024-04-20 12:23:04 +01:00
adf62d4483 arhcive: make it work when disk is attached before boot
This is a bit of a kludge (a lot of a kludge) but it will
get it running whilt I work on something better
2024-04-17 18:49:30 +01:00
68eb1360f6 use appended dtb in gl-mt300n-v2 tftpboot
probably the A variant needs this as well
2024-04-17 18:48:19 +01:00
19ad6cd278 watchdog: put s6 pkg on $PATH for s6-svstat 2024-04-17 13:01:10 +01:00
00076c7b81 mount service: use uevent-watch 2024-04-17 12:59:13 +01:00
721e7499f3 arhcive: use usb module instead of harcoded kconfig 2024-04-17 12:53:43 +01:00
fc723b9a35 think 2024-04-16 18:59:01 +01:00
a5f16dfa81 convert inout test to use uevent-watch 2024-04-15 22:15:27 +01:00
41a4b1f7ef clean cruft from inout test script 2024-04-15 22:00:44 +01:00
42a5699326 remove unneeded config from inout test 2024-04-15 21:19:18 +01:00
ea2b25168e add uevent-watch, which toggles services based on uevent msgs 2024-04-15 21:15:07 +01:00
5564cf0554 add nellie.close 2024-04-14 22:45:29 +01:00
f3a13630d3 add multicast groups param to nellie.open 2024-04-14 22:45:29 +01:00
f233acf9ff netlink uevent hello world 2024-04-14 22:45:29 +01:00
b6a054c588 add mdevd as module
following the upstream example, it republishes uevent messages
using multicast group 4 instead of group 2 as used by udev.
2024-04-14 21:59:23 +01:00
b231664a06 anoia: add basename, dirname 2024-04-11 23:11:20 +01:00
f4bf3029fa anoia: alphabetize exports 2024-04-11 23:11:13 +01:00
05f2c9a2f7 add lua in nix-shell environment 2024-04-11 23:11:06 +01:00
5df5c822ea convert mount service to trigger
Good: this means it's not hanging holding the s6 dataase lock.

Bad: it's the ugliest implementation and doesn't deserve to be preserved

(tbf the ugliness is not new)
2024-04-03 23:17:36 +01:00
4795dd05b7 unconditionally restart trigger services on liminix-rebuild
We call s6-rc -u -p default to restart/start the base services
on a rebuild, otherwise services that are only in the new
configuration won't come up. However, this stops any service
started by a trigger. So, workaround is to restart the trigger
service and expect it to restart the services it manages if they're
needed
2024-04-03 23:07:56 +01:00
a192f08881 remove missing module 2024-03-29 17:34:10 +00:00
a873dc6608 Merge commit 'efcfdcc' 2024-03-28 23:47:04 +00:00
2fb4756a7f add soft restart option to liminix-rebuild
instead of doing a full reboot, it runs activate / and uses
s6-rc-update to install the new service database
2024-03-28 23:45:10 +00:00
04f5174425 fix vanilla-configuration defaultroute 2024-03-28 22:13:21 +00:00
dca2e4def1 fix params to s6-rc-init
flags must precede scandir otherwise they're ignored
2024-03-28 21:56:28 +00:00
b60126775a improve liminix-rebuild test
* make it executable
* improve robustness
* do't hardcode services.default (why did it do this?)
2024-03-28 21:37:47 +00:00
76f11bcc93 liminix-rebuild: remove -f flag from reboot call
now we have timeouts in service definitions, shouldn't need this
any more
2024-03-28 21:37:47 +00:00
efcfdcc21d think 2024-03-28 20:59:39 +00:00
77f1a78331 ifwait block if s6-rc lock is held
otherwise it doesn't trigger the service if something else is
slow to start
2024-03-28 20:59:39 +00:00
28a5dec7dd implement ifwait trigger service and use in bridge
should we convert all ifwait uses to this trigger too? seems
reasonable
2024-03-28 20:59:39 +00:00
fad0a47b75 add config.system.callService
this is like pkgs.callService except that it passes
config.system.service as a param so that the service
being defined can invoke other services

if this proves to be a good idea, all uses of
pkgs.callService should be changed to use it instead
2024-03-28 20:59:39 +00:00
af52aafc84 deep thoughts 2024-03-28 20:59:39 +00:00
34442b6069 failing test for ifwait 2024-03-28 20:59:39 +00:00
b8a46fc05e allow buildInputs param to s6 service
this is in preparation for trigger services that need to
close over the triggered service without adding it to
s6-rc dependencies
2024-03-28 20:58:53 +00:00
8ac2c6cec1 support timeouts (default 30s) for starting s6-rc services 2024-03-28 20:58:47 +00:00
8879b2d1ba fix rt2x00 wifi 2024-03-28 20:58:39 +00:00
83e346d5a0 add deviceName param 2024-03-22 21:55:44 +00:00
156b1fe64a deep thoughts 2024-03-22 21:54:38 +00:00
1a314e55b7 firewall module: provide default rules and merge extraRules
a firewall with no configuration will get a relatively sane ruleset. a
firewall with `extraRules` will get them deep merged into the default
rules.  Specifying `rules` will override the defaults
2024-03-21 12:00:34 +00:00
9263b21faa create gateway profile by extracting from rotuer example 2024-03-21 10:04:42 +00:00
0a820a702a extneder: delete nftables kernel config
don't need nftables on a bridge. (do we? hope not)
2024-03-20 19:05:31 +00:00
4ea518e296 expose modulesPath to ease out-of-tree configuration.nix 2024-03-20 18:58:44 +00:00
98318b450d deep thoughts 2024-03-16 20:16:49 +00:00
e4ac7f19dc fix ifwait deps 2024-03-16 20:16:49 +00:00
9c22744850 deep thoughts 2024-03-16 20:16:49 +00:00
c697be8c28 temporary fix for cmake cross-compilation 2024-03-16 20:16:49 +00:00
dan
202a37221a Merge pull request 'tftpboot: use commandLineDtbNode' () from flokli/liminix:tftpboot-honor-commandLineDtbNode into main
Reviewed-on: 
2024-03-16 18:18:18 +00:00
436eb03a7b tftpboot: use commandLineDtbNode
config.boot.commandLineDtbNode can be set from `bootargs` to
`bootargs-override` (used for boards where the u-boot on the board does
set `bootargs` on its own).

In that case, the code updating the cmdline for tftpboot purposes also
needs to update this node, not the `bootargs` node.

Otherwise the kernel won't find the phram device, as it never heard
about it, as it didn't get the necessary cmdline options.
2024-03-16 20:06:38 +02:00
e5963ae3f7 deep thoughts 2024-03-06 23:19:47 +00:00
f164f19d95 service starts and stops 2024-03-06 23:19:47 +00:00
dd4ab41f6a rename run-event 2024-03-06 23:19:47 +00:00
5d5dff6729 WIP add failing test that service starts 2024-03-06 23:19:47 +00:00
570d29c368 pass command line params to run instead of reffing global 2024-03-06 23:19:47 +00:00
725af00dc9 improve test for dummy0 up
if we run off the end of the events fixture, it didn't work
2024-03-06 23:19:47 +00:00
e1b932ec27 remove hardcoded filename in test event generator 2024-03-06 23:19:47 +00:00
7173b6fb1c don't call os.exit 2024-03-06 23:19:47 +00:00
ed9548f21d pass event producer fn as param 2024-03-06 23:19:47 +00:00
0787807a7f ifwait: don't run on load if in test harness 2024-03-06 23:19:47 +00:00
38ed91f641 simplify assertion 2024-03-06 23:19:47 +00:00
ffe9603c39 remove file-scoped parameters var 2024-03-06 23:19:47 +00:00
cbd3dfefc5 ifwait fixture/test harness 2024-03-06 23:19:47 +00:00
018c1868b5 ifwait: use anoia.assoc 2024-03-06 23:19:47 +00:00
5184ff63f7 add anoia.nl, a convenience wrapper on netlink 2024-03-06 23:19:47 +00:00
35909c9a23 add netlink to fennelrepl 2024-03-06 23:19:47 +00:00
4383462199 deep thoughts 2024-03-06 23:19:47 +00:00
9730cdd63b add assoc to anoia 2024-03-06 23:19:47 +00:00
dan
095853214b Merge pull request 'Fix kernel build on belkin' () from sinavir/liminix:fix_kernel_build_on_belkin into main
Reviewed-on: 
2024-03-06 18:21:13 +00:00
9d6e50cbbc extract extneder example to a "profile"
this is a bit of an experiment to reduce the copy-paste in
examples by turning them into "application" modules.

planning to follow up with another module for "wifi router"
2024-02-27 23:13:12 +00:00
94dbc56595 fix doc 2024-02-27 20:08:30 +00:00
2cd7f932eb alignment may be null 2024-02-27 19:47:46 +00:00
sinavir
27c7735f02 belkin-RT3200: fix kernel options 2024-02-22 21:57:40 +01:00
sinavir
29c9de248d fix import of openwrt sources 2024-02-22 21:57:33 +01:00
3ca0d87c27 ci.nix: alphabetise systems 2024-02-21 19:49:14 +00:00
8f30db58ae New port to Zyxel NWA50AX: update NEWS and ci.nix 2024-02-21 19:32:50 +00:00
f9ab0590a6 Merge remote-tracking branch 'raito/nwa50ax' 2024-02-21 19:27:23 +00:00
84fa8d65f4 fennel: system: verbose log of command that was run 2024-02-21 19:27:14 +00:00
9b0149ecb7 deep thoughts 2024-02-21 19:26:33 +00:00
Raito Bezarius
baf3cf7413 devices/zyxel-nwa50ax: fix dual image mgmt after DTB expansion
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 03:13:35 +01:00
Raito Bezarius
c5145b5fc9 devices/zyxel-nwa50ax: make zyxel-bootconfig executable
Otherwise, it doesn't work well…

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 03:13:21 +01:00
Raito Bezarius
628f4dfdbe devices/zyxel-nwa50ax: developer todo
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 03:13:21 +01:00
Raito Bezarius
da59e2a349 devices/zyxel-nwa50ax: complete documentation
It covers everything I know more or less.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:57:34 +01:00
Raito Bezarius
c0a9571a13 devices/zyxel-nwa50ax: upgrade MT7915 firmware from OpenWRT repository
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:57:34 +01:00
Raito Bezarius
d6ffdd7be6 devices/zyxel-nwa50ax: expose primary and secondary images
To support A/B a bit better.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:57:34 +01:00
Raito Bezarius
985f982435 examples/nwa50ax-ap: support bridge between lan and ethernet
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
a893c0dc4c devices/zyxel-nwa50ax: use our own more advanced DTB
OpenWRT had a DTB for the NWA50AX LEDs that I didn't pick up.

Anyway, we need to include our own special DTB for the NWA platform in general
to support A/B operations, because OpenWRT original one just mark everything else read-only.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
3ec29dc1b9 examples/nwa50ax-ap: ensure mtdutils is available for further flashing
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
0e81953b67 devices/zyxel-nwa50ax: cleanup of flash attribute and rootDevice
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
3c70a0d037 devices/zyxel-nwa50ax: ensure bridge is always available
Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
422f3edab1 modules/zyxel-dual-image: init
This adds a simple boot blessing module, to be used, with the Zyxel NWA50AX.

There's a lot of elephant in the rooms: how do you upgrade kernel, etc.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
c14b2f6356 modules/busybox: add dhcprelay
This enables to run a DHCP relay from multiple interfaces.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
cdafff2095 examples/nwa50ax-ap: init
This is a quite comprehensive example using maximally the hardware
available to reach nice performance.

In the future, I will even add RADIUS examples.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-19 02:48:50 +01:00
Raito Bezarius
13f1bb9f52 devices/zyxel-nwa50ax: init 2024-02-19 02:48:48 +01:00
Raito Bezarius
019fef6929 zyxel-bootconfig: init at no version
This tool is useful for manipulating the A/B boot status of the image.
2024-02-18 20:30:41 +01:00
Raito Bezarius
63007859c2 modules/outputs/zyxel-nwa-fit: init
Zyxel "firmware" format is just… a FIT with some metadata on the models.

This FIT is like this:

--------------------------
    uImage FIT header
--------------------------
    Linux kernel
--------------------------
    FDT DTB
--------------------------
    Padding so that
    this makes
    8192kb [1]
--------------------------
    UBI volume
    as a root filesystem
--------------------------

We just reproduce this in a very brutal and naive way.
In the future, this seems worth to generalize and modularize this idea
so that zyxel-nwa-fit is just an instance of a more general output.

[1]: https://git.openwrt.org/?p=openwrt/openwrt.git;a=blob;f=target/linux/ramips/image/mt7621.mk;h=ab1b829ba0086cb9fc9ca8cbbf3cbc14735034d6;hb=refs/heads/main#l3097

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-18 20:30:41 +01:00
Raito Bezarius
e9ab8d7183 modules/outputs/ubivolume: introduce ubinization
It creates an UBI image based on an UBI volume configuration.

For now, it creates only an empty rootfs.
2024-02-18 20:30:41 +01:00
Raito Bezarius
3dc58de0eb modules/outputs: expose commandLineDtbNode option
We allow `bootargs` and `bootargs-override` for now only.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-18 20:30:41 +01:00
Raito Bezarius
dde8386f75 builders/uimage: support aligning the FIT
This is necessary when writing to a MTD partition with a certain erasesize.
2024-02-18 20:30:41 +01:00
Raito Bezarius
c59364d623 modules/outputs/ubifs: expose rootubifs rather than rootfs
I believe there should be another module exposing `rootubifs` as `rootfs`
or let any other module just subsume that component like `zyxel-nwa-fit` output.

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-18 20:30:41 +01:00
Raito Bezarius
b76c5b4abe modules/ubifs: revamp to offer directly access to the UBIfs partition
Adds the LEB and PEB option and let the user remove the boot image in case
where U-Boot does not support UBI boot.
2024-02-18 20:30:41 +01:00
Raito Bezarius
0a8343be66 pkgs/kernel/uimage: introduce commandLineDtbNode
Certain devices like the Zyxel NWA50AX will pass information on the command-line
to explain what is the current image (`bootImage=1` vs. `bootImage=0`).

Unfortunately, if we set the `chosen/bootargs` node, this will be overridden forcibly
by U-Boot.

To avoid this problem, it's easier to simply just use another DTB node like `bootargs-override` which
is what OpenWRT does [1].

[1]: https://git.openwrt.org/?p=openwrt/openwrt.git;a=blob;f=target/linux/ramips/patches-5.15/314-MIPS-add-bootargs-override-property.patch;h=e7dca7af886e8c0b69ba2b23f5855ddfeeb0d4a1;hb=refs/heads/main

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-18 20:30:41 +01:00
Raito Bezarius
d14ee41325 liminix-rebuild: use -f flag to reboot effectively
My AP does not reboot upon `reboot` but `reboot -f`… why?

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-17 11:45:17 +00:00
Raito Bezarius
8f814658fe hostapd: enable 802.11ax
For people enjoying WiFi 6 heaven… :>

Signed-off-by: Raito Bezarius <masterancpp@gmail.com>
2024-02-17 11:45:10 +00:00
60508f4d4e update NEWS
* Turris Omnia
* possible wifi regressions
2024-02-16 21:00:59 +00:00
ca64e9035e gl-ar750 ath9k needs cal data from MTD 2024-02-16 20:44:56 +00:00
4bcc3d5b28 dhcpc6 scripts: simplify (and improve correctness) 2024-02-16 18:47:12 +00:00
28fe37d555 deep thoughts 2024-02-16 18:30:54 +00:00
175db9f604 tail -F for rotuer 2024-02-16 18:30:24 +00:00
b5722a0153 gl-ar750: ath10k wireless depends on firmware
so make sure the firmware is present _first_

the ath10k is still broken anyway, looking into why
2024-02-16 00:38:36 +00:00
c373152673 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 doesn't work in QEMU but I think the problem is with the
U-Boot configuration for QEMU. It does work on at least one
hardware device so I'm pushing it anyway

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

Co-authored-by:  Arnout Engelen <arnout@bzzt.net>
2024-02-15 23:44:47 +00:00
7e7171556f subdue dnsmasq logs
we need to find a better way of doing this. people might _want_
to have All The Logs from evey dns query
2024-02-15 23:43:54 +00:00
6920ee765d deep thoughts 2024-02-15 09:11:54 +00:00
71a1ef286e deep thoughts 2024-02-13 22:32:57 +00:00
ffe0e9d26b use mkstate for dropbear keys 2024-02-13 22:12:26 +00:00
2b22c7aa91 dnsmasq: store dhcp lease file on /persist 2024-02-13 21:54:45 +00:00
3c950704e1 rename /run/service-state to /run/services/outputs 2024-02-13 21:41:43 +00:00
8578a554c7 deep thoughts 2024-02-13 21:11:30 +00:00
3851698d35 fix tftpboot compressed rootfs 2024-02-13 18:16:17 +00:00
f69ebbb6f5 fix doc CI target 2024-02-13 15:41:45 +00:00
16e4b05653 dhcp6c: set preferred and valid address lifetimes
also workaround a bug in rebinding/updates where we get an error
from "ip addr add" trying to add an address that's already present
2024-02-13 13:49:12 +00:00
8ac848b1e6 ath10k_pci: wifi modules must be modules 2024-02-13 12:56:03 +00:00
b7efbd3e21 update NEWS file 2024-02-12 21:10:52 +00:00
a654577ac2 improve port-forwarding comment 2024-02-12 21:05:01 +00:00
c50423f689 turris omnia: upgrade to mainline 6.7.4 kernel
On this device we don't need the openwrt kernel or patches. The
newer kernel also fixes the weird one minute pause at boot when
it was doing something with either mmc or switch.
2024-02-12 20:43:01 +00:00
65479e206b use regular kernel not backports for mac80211
the kernel on most devices is now newer than the version that the
backported drivers were backported from
2024-02-12 20:41:10 +00:00
79926c6fe7 remove call to deleted package 2024-02-12 14:56:12 +00:00
ae4856ea7c improve firewall comment 2024-02-12 13:56:56 +00:00
b9c0d93670 build modules at same time as main kernel vmlinux
This changes the practice for building kernel modules: now we expect
that the appropriate Kconfig symbols are set to =m in
config.kernel.config, and then use pkgs.kmodloader to create
a service that loads and unloads all the modules depended on by
a particular requirement.

Note that modules won't be installed on the target device just by
virue of having been built: only the modules that are referenced by a
kmodloader package will be in the closure.

An example may make this clearer: see modules/firewall/default.nix
in this commit.

Why?

If you have a compiled Linux kernel source tree and you change some
symbol from "is not set" to m and then run make modules, you cannot in
general expect that newly compiled module to work. This is because
there are places in the build of the main kernel where it looks to see
which modules _may_ be defined and uses that information to
accommodate them.

For example in an in-kernel build of

  https://github.com/torvalds/linux/blob/master/net/netfilter/core.c#L689

some symbols are defined only if CONFIG_NF_CONNTRACK is set, meaning
this code won't work if we have it unset initially then try later to
enable it and build modules only. Or see

  https://github.com/torvalds/linux/blob/master/include/linux/netdevice.h#L160
2024-02-11 23:47:11 +00:00
11287a8436 allow lan dns queries (ipv6) 2024-02-11 23:32:46 +00:00
57aece0709 rotuer: don't forward queries for local domain 2024-02-11 23:32:46 +00:00
c1d285a220 rotuer: network debugging tools 2024-02-11 23:32:46 +00:00
dce983ec79 move kernel module to its own subdir 2024-02-11 18:15:55 +00:00
812f497660 add kernel.version param to allow for version-specific patches
default to 5.15.137 to avoid breaking the devices that don't declare it
2024-02-11 16:19:52 +00:00
1206d02200 rotuer-secrets: remove root_password, add wifi ssid and domainName
this is step one towards getting rid of rotuer-secrets completely and
turning rotuer into a "profile" module that can be less hackily
customised for other people's networks
2024-02-11 15:56:14 +00:00
7c196bf9b4 rotuer: make 5GHz wifi faster
VHT doesn't work unless HT is enabled, apparently
2024-02-11 15:38:19 +00:00
86d19c54b3 turris omnia kernel: add RTC, i2c mux, eeprom 2024-02-09 22:34:46 +00:00
aca3e11631 firewall: make ipv4 work 2024-02-08 23:15:48 +00:00
dan
273c66b2d3 Merge pull request 'Add support for TP-Link Archer AX23' () from raboof/liminix:add-archer-ax23-v1-bak into main
Reviewed-on: 
2024-02-08 17:47:46 +00:00
87f6a31a06 improve firewall log format 2024-02-08 17:21:26 +00:00
a9ea01428e firewall: don't drop in conntrack rule
as there are other rules following that might want to accept
2024-02-08 17:20:39 +00:00
92b0bec038 rotuer: add schnapps and the rest of the lan interfaces 2024-02-07 23:48:10 +00:00
82537bbe68 delete commented-out code 2024-02-07 23:47:38 +00:00
efb29c5901 demo-firewall: add some rules for ipv4 2024-02-07 23:47:09 +00:00
29e61be26c rotuer: get lan rfc1918 prefix from secrets 2024-02-07 23:46:16 +00:00
6f1f9d6f20 firewall: fix module loading 2024-02-07 23:43:41 +00:00
34291292c0 fix dependency on kernel moduels in firewall service 2024-02-07 16:21:14 +00:00
c9e4c1b0da kernel-modules: use linuxArch instead of case expression 2024-02-07 16:20:34 +00:00
Arnout Engelen
63e3f2aa58
Add support for TP-Link Archer AX23 2024-02-06 18:00:55 +01:00
Arnout Engelen
61494fdc0c
Add tplink module for creating 'safeloader' images 2024-02-06 17:59:38 +01:00
891d6e5f20 thenk 2024-02-05 19:20:13 +00:00
dan
c4041b00f6 Merge pull request 'docs: add hardware recommendation' () from raboof/liminix:hardware-recommendations into main
Reviewed-on: 
2024-02-05 15:56:07 +00:00
f875622100 improve formatting 2024-02-04 18:24:01 +00:00
49ec4a2961 installation instructions for Turris Omnia
feels like a milestone, or at least a big step towards one
2024-02-04 18:20:04 +00:00
c8154a2db9 kernel: add "conditional" config
imagine: you are using a device that requires
CONFIG_MYDEVICE_FROBOZZ_DRIVER but only if CONFIG_FROBOZZ has been
specified elsewhere. Because we check that every requested config
symbol actually appears in .config then it can't be added
unconditionally or the build will fail if CONFIG_FROBOZZ wasn't asked
for.

I'm not 100% happy about this design but it's the best I've thought of
so far.
2024-02-04 18:12:15 +00:00
02cf2c6b80 add ssh keys in recovry image 2024-02-04 18:10:58 +00:00
b0709a6443 systemconfig: fix missing backslashes on env vars 2024-02-04 17:19:03 +00:00
86f5c9b568 schnapps needs util-linux for mount
specifically, it expects mount /dev/foo -o blah /dest to work,
but busybox mount expects options to precede all the other
command line args
2024-02-04 15:50:25 +00:00
ef707de8b1 add extlinux in recovery example
this needlessly bloats the TFTP image, which is a shame, but is
needed for installing onto usb stick
2024-02-02 19:51:41 +00:00
89c88dd472 specify type for rootDevice module option 2024-02-02 19:50:13 +00:00
c1ad139310 whitespace 2024-02-02 19:43:34 +00:00
f682b26c29 omnia seems very fussy about tftp load address
when loading with 0x1000000 base address, something was getting
corrupted in the uncompressed rootfs

$ head -c $(printf "%d" 0x2be0000) rootfs | sha1sum
142571fe0436c18191727d1d4c2fd32163c1f2e1  -
=> sha1sum 0x1000000 2be0000
sha1 for 01000000 ... 03bdffff ==> 142571fe0436c18191727d1d4c2fd32163c1f2e1

but!

$  head -c $(printf "%d" 0x2bf0000) rootfs | sha1sum
7aa004ba87c6772bade491fbade164e2dfe100f9  -
=> sha1sum 0x1000000 2bf0000
sha1 for 01000000 ... 03beffff ==> 1a0923a94784d0c0b86006c5e6fff1649770dad3
2024-02-02 19:36:11 +00:00
84ce618213 recovery: grow fs to partition size before starting sshd
sshd expects there to be space in /persist/secrets that it can
use to write host keys, but when we make ext4fs images we don't
put any free space in them
2024-01-28 11:30:19 +00:00
9e199c6957 tftpboot: compute dtbSize *after* changing dtb
Adding the reserved-memory node to the dtb can cause it to grow
by enough that it needs an extra page - this will overlap the start
of the kernel image if we calculate offsets based on the original size

Reported-by: sinavir
Authored-by: sinavir
2024-01-26 22:51:58 +00:00
c8e3d84bf4 think 2024-01-26 22:46:36 +00:00
dd8ec18881 restore boot.tftp.freeSpaceBytes 2024-01-26 22:46:36 +00:00
1730cf07b1 bug workaround
If we set squashfs rootfsType, the image doesn't rebuild when
the kernel config is changed. Need to figure out why
2024-01-26 22:46:36 +00:00
de51bfe13d default root device in recovery to sda1
It will probably work fine for USB-stick boot (except in the case
where there is > 1 usb device plugged in, so maybe don't do that)

It doesn't matter for TFTP boot because boot.scr overrides the root=
param anyway
2024-01-26 22:46:36 +00:00
b09723345c don't put all of util-linux in recovery
it adds ~ 5MB to the image size
2024-01-26 22:46:36 +00:00
1781d4b6e4 add lzma to buildenv 2024-01-26 22:46:36 +00:00
c219350d7c add usb storage for turris omnia
ideally we would make this a module instead of compiling in
directly
2024-01-26 22:46:36 +00:00
dan
6f83282ff5 Merge pull request 'openwrt: update to v23.05.2' () from raboof/liminix:openwrt-update-to-v23.05.2 into main
Reviewed-on: 
2024-01-26 22:39:11 +00:00
Arnout Engelen
04895f9cf6
openwrt: update to v23.05.2 2024-01-25 12:29:03 +01:00
dan
5f2d1660bd Merge pull request 'belkin-rt3200: Enable watchdog drivers' () from sinavir/liminix:add_belkin_watchdog into main
Reviewed-on: 
2024-01-24 14:36:08 +00:00
sinavir
7642e23c0a belkin-rt3200: Enable watchdog drivers 2024-01-24 13:38:55 +01:00
83ee488e4c systemconfig: /boot needs to go inside /persist 2024-01-09 13:10:02 +00:00
f19a937eda omnia needs MARVELL_PHY for the WAN port to work
interestingly, we only see this when it boots from mmc, because
for tftpboot the bootloader has already performed negotiation
and therefore it works despite the missing option.
2024-01-09 13:07:14 +00:00
f0490f37d5 turris omnia: tidy config, remove dead bits 2024-01-08 19:22:42 +00:00
c1101d3af5 make extlinux work with liminix-rebuild
add /boot to the systemConfiguration closure
2024-01-08 18:58:07 +00:00
9a3d7a387e correct turris omnia root device 2024-01-08 18:55:41 +00:00
228c0a1668 pass rootOptions config as rootflags= kernel cmdline opt 2024-01-08 18:54:49 +00:00
63f034e362 preinit: parse rootflags= in kernel command line 2024-01-08 00:35:13 +00:00
6971d03520 preinit: check return from write() 2024-01-07 21:24:16 +00:00
7bc9cb6c55 why is extlink hardcoding root device? 2024-01-07 20:30:23 +00:00
a251ceeb99 omnia releng 2024-01-07 16:54:44 +00:00
38a7f0b03b turris omnia: add all lan devices
I think we might turn "lan" into a bridge, but that's for later
2024-01-07 16:54:44 +00:00
c0c4752350 systemconfig "install" cmd honours prefix on source 2024-01-07 16:54:44 +00:00
3c941b4ce2 partial btrfs support
doesn't actually know how to make the filesystem, just
kernel config and accept it as a valid option
2024-01-07 16:43:43 +00:00
243295aab8 recovery config for turris omnia 2024-01-07 14:58:46 +00:00
45e8db09e1 liminix-rebuild: escape brackets in usage message 2024-01-07 14:18:19 +00:00
2a93f24a58 add turris "schnapps" tool
in its current state this is useful for turris omnia only, but will
allow us to do installs and rollback to turris os if needed.
2024-01-05 00:07:01 +00:00
64898eada8 mount tmpfs on /tmp
too much stuff doesn't work without it and it's not
all worth patching
2024-01-04 23:22:02 +00:00
Arnout Engelen
d5026c2074
docs: add hardware recommendation
Also add infrastructure to also generate the supported hardware
page when building the docs locally
2024-01-04 14:35:00 +01:00
136c5e6f32 alphabetize package list 2024-01-04 10:15:23 +00:00
fa9a2c6413 add btrfs-progs 2024-01-04 09:33:44 +00:00
049cdbb610 turris omnia: don't hardcode rootfsType 2024-01-03 20:18:07 +00:00
5ee4adff10 NEWS: we now expect Liminix 23.11 2024-01-03 19:44:49 +00:00
1747 changed files with 15652 additions and 2127 deletions
.gitignoreNEWSREADME.mdTHOUGHTS.txtboot.expectbordervm-configuration.nixbordervm.conf-example.nixci.nixdefault.nix
devices
doc
examples
modules

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ result-*
_build
*-secrets.nix
examples/static-leases.nix
/doc/hardware.rst

132
NEWS
View File

@ -23,5 +23,137 @@ the name of an outputs that gloms together other filesystem-like
outputs with some kind of partition table - so we might in future have
gptimage or lvmimage or ubimage.
2024-01-03
Liminix is now targeted to Nixpkgs 23.11 (not 23.05 as previously).
Upstream changes that have led to incompatible Liminix changes are:
* newer U-Boot version
* util-linux can now be built (previously depended on systemd)
2024-01-30
New port! Thanks to Arnout Engelen <arnout@bzzt.net>, Liminix
now runs on the TP-Link Archer AX23.
2024-02-12
* 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.
* 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
them afterwards as though they were "out of tree". Refer to commit
b9c0d93670275e69df24902b05bf4aa4f0fcbe96 for a fuller explanation
of how this simplifies things.
2024-02-13
So that we can be more consistent about services that would like their
state to be preserved across boots (assuming a writable filesystem)
these changes have been made
* /run/service-state has been moved to /run/services/outputs
to better reflect what it's used for
* /run/services/state is either a symlink to /persist/services/state
(if there's a writeable fs on /persist) or a directory (if there
isn't)
The change will lose your ssh host key(s) unless you copy them from
the old location to the new one before rebooting into the new system
mkdir -m 02751 -p /run/services/state/dropbear
cp /persist/secrets/dropbear/* /run/services/state/dropbear
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;
};
})
];
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.
2024-8-16
As part of implementing log shipping, the default directory for system
logs has beenchanged from /run/uncaught-logs to /run/log
2024-10-09
liminix-rebuild is being deprecated. From hereon in, the preferred way
to do an incremental update on an installed device with a writable
filesystem is to build the systemConfiguration output
nix-build -I liminix-config=hosts/myhost.nix --argstr deviceName turris-omnia -A outputs.systemConfiguration
and then run the generated `install.sh` script
result/install.sh root@192.168.8.1
2024-12-16
Config options changed: if you had set config.hardware.dts.includes
(maybe in an out-of-tree device port) to specify the search paths
in which dtc finds include files, you will need to change this to
hardware.dts.includePaths.
The "new" hardware.dts.includes option is now for dtsi files which
should be merged into the device tree.
2024-12-19
Incremental updates changed again (but not massively). From hereon in,
the preferred way to do an incremental update on an installed device
with a writable filesystem is to build the updater output
nix-build -I liminix-config=hosts/myhost.nix --argstr deviceName turris-omnia -A outputs.updater
and then run the generated `update.sh` script. See
https://www.liminix.org/doc/admin.html#updating-an-installed-system
2024-12-22
outputs.zimage is now outputs.kernel.zImage. This is unlikely to
affect many people at all but I mention it anyway.

View File

@ -33,7 +33,7 @@ functioning version, see [the CI system](https://build.liminix.org/jobset/limini
Documentation is in the [doc](doc/) directory. You can build it
by running
nix-shell -p sphinx --run "make -C doc html"
nix-shell -p sphinx --run "make -C doc hardware.rst html"
Rendered documentation corresponding to the latest commit on `main`
is published to [https://www.liminix.org/doc/](https://www.liminix.org/doc/)

File diff suppressed because it is too large Load Diff

38
boot.expect Normal file
View File

@ -0,0 +1,38 @@
# 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
fconfigure stderr -buffering none
fconfigure stdout -buffering none
proc waitprompt { } {
expect {
"BusyBox" { puts stderr "DONE\r"; exit 0 }
"READY" { puts stderr ";;; READY\r"; }
timeout { puts stderr ";;; timed out waiting after $line\r" }
}
}
proc sendline { line } {
send "$line; echo \$ready \r"
}
log_user 0
log_file -a -open stderr
set f [open "result/boot.scr"]
send "setenv ready REA\rsetenv ready \${ready}DY\r"
set timeout 300
expect_before timeout abort
while {[gets $f line] >= 0} {
puts stderr ";;; next line $line\r"
puts stderr ";;; waiting for prompt\r"
puts stderr ";;; sending\r"
sendline $line
waitprompt
}
puts stderr "done\r\n"
close $f

View File

@ -4,6 +4,10 @@ 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 ''
@ -51,18 +55,17 @@ 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 = {
@ -76,29 +79,69 @@ 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";
};
};
services.nginx = {
enable = true;
user = "liminix";
virtualHosts.${config.networking.hostName} = {
root = "/home/liminix";
default = true;
};
};
systemd.services.nginx.serviceConfig.ProtectHome = "read-only";
systemd.services.sshd.wantedBy = pkgs.lib.mkForce [ "multi-user.target" ];
virtualisation = {
forwardPorts = [
{
from = "host";
host.port = 7654;
# guest.address = "10.0.2.15";
guest.port =7654;
}
{
host.port = 2222;
guest.address = "10.0.2.15";
guest.port = 22;
}];
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";
};
};
};
services.tang = {
enable = true;
ipAddressAllow = [ "10.0.0.0/24" "0.0.0.0/0" ];
};
environment.systemPackages =
let wireshark-nogui = pkgs.wireshark.override { withQt = false ; };
in with pkgs; [
@ -108,6 +151,8 @@ in {
tufted
iptables
usbutils
busybox
clevis
];
security.sudo.wheelNeedsPassword = false;
networking = {
@ -117,11 +162,17 @@ 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"];
extraGroups = [ "wheel" ];
openssh.authorizedKeys.keys = cfg.keys;
};
services.getty.autologinUser = "liminix";
};

View File

@ -1,8 +1,12 @@
{...}:
{ ... }:
{
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";
};

90
ci.nix
View File

@ -1,76 +1,80 @@
{
nixpkgs
, unstable
, liminix
, ... }:
let
inherit (builtins) map;
pkgs = (import nixpkgs {});
borderVmConf = ./bordervm.conf-example.nix;
pkgs = import <nixpkgs> { };
liminix = <liminix>;
borderVmConf = ./bordervm.conf-example.nix;
inherit (pkgs.lib.attrsets) genAttrs;
devices = [
"gl-ar750" "gl-mt300n-v2" "gl-mt300a"
"qemu" "qemu-aarch64" "qemu-armv7l"
"gl-ar750"
"gl-mt300a"
"gl-mt300n-v2"
"qemu"
"qemu-aarch64"
"qemu-armv7l"
"tp-archer-ax23"
"zyxel-nwa50ax"
"turris-omnia"
];
vanilla = ./vanilla-configuration.nix;
for-device = name:
(import liminix {
inherit nixpkgs borderVmConf;
inherit borderVmConf;
device = import (liminix + "/devices/${name}");
liminix-config = vanilla;
}).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 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 borderVmConf;
device = import (liminix + "/devices/qemu");
liminix-config =
{ ... }:
{
imports = [ ./modules/all-modules.nix ];
};
}).outputs.optionsJson;
installers = map (f: "system.outputs.${f}") [
"vmroot"
"mtdimage"
"ubimage"
];
inherit (pkgs.lib) concatStringsSep;
in pkgs.stdenv.mkDerivation {
}).outputs.optionsJson;
in
pkgs.stdenv.mkDerivation {
name = "liminix-doc";
nativeBuildInputs = with pkgs; [
gnumake sphinx fennel luaPackages.lyaml
gnumake
sphinx
fennel
luaPackages.lyaml
];
src = ./.;
buildPhase = ''
cat ${json} | fennel --correlate doc/parse-options.fnl > doc/modules-generated.rst
cat ${json} | fennel --correlate doc/parse-options-outputs.fnl > doc/outputs-generated.rst
cat ${json} | fennel --correlate doc/parse-options.fnl > doc/modules-generated.inc.rst
cat ${json} | fennel --correlate doc/parse-options-outputs.fnl > doc/outputs-generated.inc.rst
cp ${(import ./doc/hardware.nix)} doc/hardware.rst
make -C doc html
'';
installPhase = ''
mkdir -p $out/nix-support $out/share/doc/
cd doc
cp *-generated.rst $out
cp *-generated.inc.rst hardware.rst $out
ln -s ${json} $out/options.json
cp -a _build/html $out/share/doc/liminix
echo "file source-dist \"$out/share/doc/liminix\"" \
> $out/nix-support/hydra-build-products
'';
};
with-unstable = (import liminix {
nixpkgs = unstable;
inherit borderVmConf;
device = import (liminix + "/devices/qemu");
liminix-config = vanilla;
}).outputs.default;
};
in jobs
in jobs //
{
all = pkgs.mkShell {
name = "all tests";
contents = pkgs.lib.collect pkgs.lib.isDerivation jobs;
};
}

View File

@ -1,35 +1,47 @@
{
device
, liminix-config ? <liminix-config>
, nixpkgs ? <nixpkgs>
, borderVmConf ? ./bordervm.conf.nix
deviceName ? null,
device ? (import ./devices/${deviceName}),
liminix-config ? <liminix-config>,
borderVmConf ? ./bordervm.conf.nix,
imageType ? "primary",
}:
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
./modules/klogd.nix
device.module
liminix-config
./modules/s6
./modules/users.nix
./modules/outputs.nix
{
boot.imageType = imageType;
}
];
};
config = eval.config;
@ -37,7 +49,14 @@ let
borderVm = ((import <nixpkgs/nixos/lib/eval-config.nix>) {
system = builtins.currentSystem;
modules = [
({ ... } : { nixpkgs.overlays = [ overlay ]; })
{
nixpkgs.overlays = [
(final: prev: {
go-l2tp = final.callPackage ./pkgs/go-l2tp {};
tufted = final.callPackage ./pkgs/tufted {};
})
];
}
(import ./bordervm-configuration.nix)
borderVmConf
];
@ -67,6 +86,8 @@ in {
go-l2tp
min-copy-closure
fennelrepl
lzma
lua
];
};
}

View File

@ -4,15 +4,14 @@
******************************
This device is based on a 64 bit Mediatek MT7622 ARM platform,
and is "work in progress" in Liminix.
and is mostly feature-complete in Liminix but as of Dec 2024
has seen very little actual use.
.. note:: The factory flash image contains ECC errors that make it
incompatible with Liminix: you need to use the `OpenWrt
incompatible with Liminix: use the `OpenWrt
UBI Installer <https://github.com/dangowrt/owrt-ubi-installer>`_ to
rewrite the partition layout before you can flash
Liminix onto it (or even use it with
:ref:`system-outputs-tftpboot`, if you want the wireless
to work).
rewrite the partition layout before you can use
Liminix with it
Hardware summary
================
@ -27,10 +26,76 @@
Installation
============
Installation is currently a manual process (you need a :ref:`serial <serial>` conection and
TFTP) following the instructions at :ref:`system-outputs-ubimage`
Liminix on this device uses the UBI volume management system to
perform wear leveling on the flash. This is not set up from the
factory, so a one-time step is needed to prepare it before Liminix
can be installed.
'';
Preparation
-----------
To prepare the device for Liminix you first need to use the
`OpenWrt UBI Installer
<https://github.com/dangowrt/owrt-ubi-installer>`_ image to
rewrite the flash layout. You can do this in one of two ways:
either follow the instructions to do it through the vendor web
interface, or you can drop to U-boot and use TFTP
.. code-block:: console
MT7622> setenv ipaddr 10.0.0.6
MT7622> setenv serverip 10.0.0.1
MT7622> tftpboot 0x42000000 openwrt-mediatek-mt7622-linksys_e8450-ubi-initramfs-recovery-installer.itb
MT7622> bootm 0x42000000
This will write the new flash layout and then boot into a
"recovery" OpenWrt installation.
Building/installing Liminix
----------------
The default target for this device is ``outputs.ubimage`` which
makes a ubifs image suitable for use with :command:`ubiupdatevol`.
To write this to the device we use the OpenWrt recovery system
installed in the previous step. In this configuration the
device assigns itself the IP address 192.168.1.1/24 on its LAN
ports and expects the connected computer to have 192.168.1.254
.. warning:: The `ubi0_7` device in these instructions is correct
as of Dec 2024 (dangowrt/owrt-ubi-installer commit
d79e7928). If you are installing some time later, it
is important to check the output from
:command:`ubinfo -a` and make sure you are updating
the "liminix" volume and not some other one which had
been introduced since I wrote this.
.. code-block:: console
$ nix-build -I liminix-config=./my-configuration.nix --arg device "import ./devices/belkin-rt3200" -A outputs.default
$ cat result/rootfs | ssh root@192.168.1.1 "cat > /tmp/rootfs"
$ ssh root@192.168.1.1
root@OpenWrt:~# ubimkvol /dev/ubi0 --name=liminix --maxavsize
root@OpenWrt:~# ubinfo -a
[...]
Volume ID: 7 (on ubi0)
Type: dynamic
Alignment: 1
Size: 851 LEBs (108056576 bytes, 103.0 MiB)
State: OK
Name: liminix
Character device major/minor: 250:8
root@OpenWrt:~# ubiupdatevol /dev/ubi0_7 /tmp/rootfs
root@OpenWrt:~# fw_setenv orig_boot_production $(fw_printenv boot_production | sed -E 's/.+?=//')
root@OpenWrt:~# fw_setenv boot_production 'led $bootled_pwr on ; ubifsmount ubi0:liminix && ubifsload ''${loadaddr} boot/fit && bootm ''${loadaddr}'
For subsequent Liminix reinstalls, you don't need to repeat the
"Preparation" step and in fact should seek to avoid it if
possible. Updating volumes with :command:`ubiupdatevol` will
preserve the erase counters used for write levelling, so is
preferred over any kind of "factory" wipe which will reset them.
'';
system = {
crossSystem = {
@ -55,11 +120,6 @@
];
config = {
kernel = {
src = pkgs.pkgsBuildBuild.fetchurl {
name = "linux.tar.gz";
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
};
extraPatchPhase = ''
${pkgs.openwrt.applyPatches.mediatek}
'';
@ -73,7 +133,7 @@
MTK_INFRACFG = "y";
MTK_PMIC_WRAP = "y";
MTK_EFUSE="y";
NVMEM_MTK_EFUSE="y";
# MTK_HSDMA="y";
MTK_SCPSYS="y";
MTK_SCPSYS_PM_DOMAINS="y";
@ -92,7 +152,6 @@
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
@ -133,24 +192,32 @@
NET_DSA_MT7530="y";
NET_DSA_TAG_MTK="y";
PSTORE = "y";
PSTORE_RAM = "y";
PSTORE_CONSOLE = "y";
PSTORE_DEFLATE_COMPRESS = "n";
SERIAL_8250 = "y";
SERIAL_8250_CONSOLE = "y";
SERIAL_8250_MT6577="y";
# SERIAL_8250_NR_UARTS="3";
# SERIAL_8250_RUNTIME_UARTS="3";
SERIAL_OF_PLATFORM="y";
# Must enble hardware watchdog drivers. Else the device reboots after several seconds
WATCHDOG = "y";
MEDIATEK_WATCHDOG = "y";
};
conditionalConfig = {
WLAN= {
MT7615E = "m";
MT7622_WMAC = "y";
MT7915E = "m";
};
};
};
boot = {
commandLine = [ "console=ttyS0,115200" ];
tftp.loadAddress = lim.parseInt "0x4007ff28";
imageFormat = "fit";
loader.fit.enable = lib.mkDefault true; # override this if you are building tftpboot
};
rootfsType = lib.mkDefault "ubifs"; # override this if you are building tftpboot
filesystem =
let inherit (pkgs.pseudofile) dir symlink;
in
@ -165,17 +232,15 @@
hardware =
let
openwrt = pkgs.openwrt;
mac80211 = pkgs.mac80211.override {
drivers = [
"mt7615e"
"mt7915e"
];
klibBuild = config.system.outputs.kernel.modulesupport;
mac80211 = pkgs.kmodloader.override {
targets = ["mt7615e" "mt7915e"];
inherit (config.system.outputs) kernel;
};
in {
ubi = {
minIOSize = "2048";
eraseBlockSize = "126976";
logicalEraseBlockSize = "126976";
physicalEraseBlockSize = "131072";
maxLEBcount = "1024"; # guessing
};
@ -190,7 +255,7 @@
rootDevice = "ubi0:liminix";
dts = {
src = "${openwrt.src}/target/linux/mediatek/dts/mt7622-linksys-e8450-ubi.dts";
includes = [
includePaths = [
"${openwrt.src}/target/linux/mediatek/dts"
"${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/"
];
@ -206,7 +271,6 @@
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"; };

View File

@ -5,11 +5,6 @@
];
config = {
kernel = {
src = pkgs.pkgsBuildBuild.fetchurl {
name = "linux.tar.gz";
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
};
config = {
MTD = "y";
MTD_BLOCK = "y";
@ -23,12 +18,17 @@
VIRTIO_BLK = "y";
VIRTIO_NET = "y";
};
conditionalConfig = {
WLAN= {
MAC80211_HWSIM = "m";
};
};
};
hardware =
let
mac80211 = pkgs.mac80211.override {
drivers = ["mac80211_hwsim"];
klibBuild = config.system.outputs.kernel.modulesupport;
mac80211 = pkgs.kmodloader.override {
inherit (config.system.outputs) kernel;
targets = ["mac80211_hwsim"];
};
in {
defaultOutput = "vmroot";

View File

@ -52,8 +52,9 @@
'';
module = {pkgs, config, lim, ... }:
module = {pkgs, config, lim, lib, ... }:
let
inherit (lib) mkIf;
openwrt = pkgs.openwrt;
firmwareBlobs = pkgs.pkgsBuildBuild.fetchFromGitHub {
owner = "kvalo";
@ -71,9 +72,10 @@
cp $blobdir/board.bin $out/ath10k/QCA9887/hw1.0/
'';
};
mac80211 = pkgs.mac80211.override {
drivers = ["ath9k" "ath10k_pci"];
klibBuild = config.system.outputs.kernel.modulesupport;
mac80211 = pkgs.kmodloader.override {
targets = ["ath9k" "ath10k_pci"];
inherit (config.system.outputs) kernel;
dependencies = [ ath10k_cal_data ];
};
ath10k_cal_data =
let
@ -91,7 +93,6 @@
'';
};
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs.liminix.networking) interface;
in {
imports = [
../../modules/network
@ -116,23 +117,32 @@
rootDevice = "/dev/mtdblock5";
dts = {
src = "${openwrt.src}/target/linux/ath79/dts/qca9531_glinet_gl-ar750.dts";
includes = [
includePaths = [
"${openwrt.src}/target/linux/ath79/dts"
];
includes = mkIf config.logging.persistent.enable [
./pstore-ramoops.dtsi
];
};
networkInterfaces =
let inherit (config.system.service.network) link;
in {
lan = link.build { ifname = "eth0"; };
wan = link.build { ifname = "eth1"; };
lan = link.build {
ifname = "lan";
devpath = "/devices/platform/ahb/1a000000.eth";
};
wan = link.build {
ifname = "wan";
devpath = "/devices/platform/ahb/19000000.eth";
};
wlan = link.build {
ifname = "wlan0";
dependencies = [ mac80211 ];
};
wlan5 = link.build {
ifname = "wlan1";
dependencies = [ mac80211 ath10k_cal_data ];
dependencies = [ ath10k_cal_data mac80211 ];
};
};
};
@ -148,19 +158,16 @@
};
boot.tftp = {
loadAddress = lim.parseInt "0x00A00000";
appendDTB = true;
};
kernel = {
src = pkgs.pkgsBuildBuild.fetchurl {
name = "linux.tar.gz";
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
};
# Mainline linux 5.19 doesn't have device-tree support for
# this device or even for the SoC, so we use the extensive
# 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 = {
@ -211,14 +218,21 @@
WATCHDOG = "y";
ATH79_WDT = "y"; # watchdog timer
# this is all copied from nixwrt ath79 config. Clearly not all
# of it is device config, some of it is wifi config or
# installation method config or ...
EARLY_PRINTK = "y";
PRINTK_TIME = "y";
};
conditionalConfig = {
WLAN = {
WLAN_VENDOR_ATH = "y";
ATH_COMMON = "m";
ATH9K = "m";
ATH9K_AHB = "y";
ATH10K = "m";
ATH10K_PCI = "m";
ATH10K_DEBUG = "y";
};
};
};
};
}

View File

@ -0,0 +1,9 @@
/ {
reserved-memory {
ramoops@03f00000 {
compatible = "ramoops";
reg = <0x03f00000 0x10000>;
pmsg-size = <0x10000>;
};
};
};

View File

@ -45,11 +45,10 @@
module = { pkgs, config, lib, lim, ...}:
let
inherit (pkgs.liminix.networking) interface;
inherit (pkgs) openwrt;
mac80211 = pkgs.mac80211.override {
drivers = ["rt2800soc"];
klibBuild = config.system.outputs.kernel.modulesupport;
mac80211 = pkgs.kmodloader.override {
targets = ["rt2800soc"];
inherit (config.system.outputs) kernel;
};
in {
imports = [
@ -82,7 +81,7 @@
dts = {
src = "${openwrt.src}/target/linux/ramips/dts/mt7620a_glinet_gl-mt300a.dts";
includes = [
includePaths = [
"${openwrt.src}/target/linux/ramips/dts"
];
};
@ -90,19 +89,6 @@
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
@ -110,13 +96,11 @@
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";
@ -126,16 +110,13 @@
};
boot.tftp = {
loadAddress = lim.parseInt "0x00A00000";
};
appendDTB = true;
};
kernel = {
src = pkgs.fetchurl {
name = "linux.tar.gz";
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
};
extraPatchPhase = ''
${openwrt.applyPatches.ramips}
${openwrt.applyPatches.rt2x00}
'';
config = {
@ -178,6 +159,14 @@
} // lib.optionalAttrs (config.system.service ? vlan) {
SWCONFIG = "y";
};
conditionalConfig = {
WLAN = {
WLAN_VENDOR_RALINK = "y";
RT2800SOC = "m";
RT2X00 = "m";
};
};
};
};
}

View File

@ -13,7 +13,7 @@
GL.iNet GL-MT300N-v2
********************
The GL-MT300N-v2 "Mango" is is very similar to the :ref:`MT300A <GL.iNet GL-MT300A>, but is
The GL-MT300N-v2 "Mango" is is very similar to the :ref:`gl-mt300a`, but is
based on the MT7628 chipset instead of MT7620. It's also marginally cheaper
and comes in a yellow case not a blue one. Be sure your device is
v2 not v1, which is a different animal and has only half as much RAM.
@ -38,14 +38,13 @@
module = { pkgs, config, lib, lim, ...}:
let
inherit (pkgs.liminix.networking) interface;
inherit (pkgs.liminix.services) oneshot;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) openwrt;
mac80211 = pkgs.mac80211.override {
drivers = ["mt7603e"];
klibBuild = config.system.outputs.kernel.modulesupport;
mac80211 = pkgs.kmodloader.override {
targets = ["mt7603e"];
inherit (config.system.outputs) kernel;
};
wlan_firmware = pkgs.fetchurl {
url = "https://github.com/openwrt/mt76/raw/f24b56f935392ca1d35fae5fd6e56ef9deda4aad/firmware/mt7628_e2.bin";
@ -79,7 +78,7 @@
dts = {
src = "${openwrt.src}/target/linux/ramips/dts/mt7628an_glinet_gl-mt300n-v2.dts";
includes = [
includePaths = [
"${openwrt.src}/target/linux/ramips/dts"
];
};
@ -97,7 +96,7 @@
swconfig dev switch0 vlan 2 set ports '0 6t'
swconfig dev switch0 set apply
'';
down = "swconfig dev switch0 set reset";
down = "${pkgs.swconfig}/bin/swconfig dev switch0 set reset";
};
in rec {
eth = link.build { ifname = "eth0"; dependencies = [swconfig]; };
@ -122,14 +121,10 @@
# 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 = {
src = pkgs.fetchurl {
name = "linux.tar.gz";
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
};
extraPatchPhase = ''
${openwrt.applyPatches.ramips}
'';
@ -185,6 +180,15 @@
RALINK_WDT = "y"; # watchdog
MT7621_WDT = "y"; # or it might be this one
};
conditionalConfig = {
WLAN = {
WLAN_VENDOR_RALINK = "y";
WLAN_VENDOR_MEDIATEK = "y";
MT7603E = "m";
};
};
};
};
}

View File

@ -19,14 +19,14 @@
ARM targets differ from MIPS in that the kernel format expected
by QEMU is an "Image" (raw binary file) rather than an ELF
file, but this is taken care of by :command:`run.sh`. Check the
documentation for the :ref:`QEMU` (MIPS) target for more information.
documentation for the :ref:`qemu` target for more information.
'';
# this device is described by the "qemu" device
installer = "vmroot";
module = {pkgs, config, lim, ... }: {
module = { config, lim, ... }: {
imports = [
../../modules/arch/aarch64.nix
../families/qemu.nix

View File

@ -24,7 +24,7 @@
'';
installer = "vmroot";
module = {pkgs, config, lim, ... }: {
module = { config, lim, ... }: {
imports = [
../../modules/arch/arm.nix
../families/qemu.nix

View File

@ -36,7 +36,7 @@
in the Development manual.
'';
module = {pkgs, config, lib, lim, ... }: {
module = { config, lib, lim, ... }: {
imports = [
../../modules/arch/mipseb.nix
../families/qemu.nix
@ -66,7 +66,7 @@
# *correct* but it does at least boot
dts = lib.mkForce {
src = "${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/mti/malta.dts";
includes = [
includePaths = [
"${config.system.outputs.kernel.modulesupport}/arch/mips/boot/dts/"
];
};

View File

@ -0,0 +1,436 @@
{
description = ''
TP-Link Archer AX23 / AX1800 Dual Band Wi-Fi 6 Router
*****************************************************
Hardware summary
================
- MediaTek MT7621 (880MHz)
- 16MB Flash
- 128MB RAM
- WLan hardware: Mediatek MT7905, MT7975
Limitations
===========
Status LEDs do not work yet.
Uploading an image via tftp doesn't work yet, because the Archer uboot
version is so old it doesn't support overriding the DTB from the mboot
command. The tftpboot module doesn't support this yet, see
https://gti.telent.net/dan/liminix/pulls/5 for the WiP.
'';
system = {
crossSystem = {
config = "mipsel-unknown-linux-musl";
gcc = {
abi = "32";
# https://openwrt.org/docs/techref/instructionset/mipsel_24kc
arch = "24kc";
};
};
};
module = {pkgs, config, lib, lim, ... }:
let firmware = pkgs.stdenv.mkDerivation {
name = "wlan-firmware";
phases = ["installPhase"];
installPhase = ''
mkdir $out
cp ${pkgs.linux-firmware}/lib/firmware/mediatek/{mt7915,mt7615,mt7622}* $out
'';
};
in {
imports = [
../../modules/arch/mipsel.nix
../../modules/outputs/tftpboot.nix
../../modules/outputs/tplink-safeloader.nix
];
config = {
kernel = {
extraPatchPhase = ''
${pkgs.openwrt.applyPatches.ramips}
'';
config = {
# Initially taken from openwrt's ./target/linux/ramips/mt7621/config-5.15,
# then tweaked here and there
ARCH_32BIT_OFF_T="y";
ARCH_HIBERNATION_POSSIBLE="y";
ARCH_KEEP_MEMBLOCK="y";
ARCH_MMAP_RND_BITS_MAX="15";
ARCH_MMAP_RND_COMPAT_BITS_MAX="15";
ARCH_SUSPEND_POSSIBLE="y";
AT803X_PHY="y";
BLK_MQ_PCI="y";
BOARD_SCACHE="y";
CEVT_R4K="y";
CLKSRC_MIPS_GIC="y";
CLK_MT7621="y";
CLOCKSOURCE_WATCHDOG="y";
CLONE_BACKWARDS="y";
CMDLINE_BOOL="y";
COMMON_CLK="y";
COMPAT_32BIT_TIME="y";
CPU_GENERIC_DUMP_TLB="y";
CPU_HAS_DIEI="y";
CPU_HAS_PREFETCH="y";
CPU_HAS_RIXI="y";
CPU_HAS_SYNC="y";
CPU_LITTLE_ENDIAN="y";
CPU_MIPS32="y";
CPU_MIPS32_R2="y";
CPU_MIPSR2="y";
CPU_MIPSR2_IRQ_EI="y";
CPU_MIPSR2_IRQ_VI="y";
CPU_NEEDS_NO_SMARTMIPS_OR_MICROMIPS="y";
CPU_R4K_CACHE_TLB="y";
CPU_RMAP="y";
CPU_SUPPORTS_32BIT_KERNEL="y";
CPU_SUPPORTS_HIGHMEM="y";
CPU_SUPPORTS_MSA="y";
CRC16="y";
CRYPTO_DEFLATE="y";
CRYPTO_HASH_INFO="y";
CRYPTO_LIB_BLAKE2S_GENERIC="y";
CRYPTO_LIB_POLY1305_RSIZE="2";
CRYPTO_LZO="y";
CRYPTO_ZSTD="y";
CSRC_R4K="y";
DIMLIB="y";
DMA_NONCOHERENT="y";
DTB_RT_NONE="y";
DTC="y";
EARLY_PRINTK="y";
FIXED_PHY="y";
FWNODE_MDIO="y";
FW_LOADER_PAGED_BUF="y";
GENERIC_ATOMIC64="y";
GENERIC_CLOCKEVENTS="y";
GENERIC_CMOS_UPDATE="y";
GENERIC_CPU_AUTOPROBE="y";
GENERIC_FIND_FIRST_BIT="y";
GENERIC_GETTIMEOFDAY="y";
GENERIC_IOMAP="y";
GENERIC_IRQ_CHIP="y";
GENERIC_IRQ_EFFECTIVE_AFF_MASK="y";
GENERIC_IRQ_SHOW="y";
GENERIC_LIB_ASHLDI3="y";
GENERIC_LIB_ASHRDI3="y";
GENERIC_LIB_CMPDI2="y";
GENERIC_LIB_LSHRDI3="y";
GENERIC_LIB_UCMPDI2="y";
GENERIC_PCI_IOMAP="y";
GENERIC_PHY="y";
GENERIC_PINCONF="y";
GENERIC_SCHED_CLOCK="y";
GENERIC_SMP_IDLE_THREAD="y";
GENERIC_TIME_VSYSCALL="y";
GLOB="y";
GPIOLIB_IRQCHIP="y";
GPIO_CDEV="y";
GPIO_GENERIC="y";
GPIO_MT7621="y";
GRO_CELLS="y";
HANDLE_DOMAIN_IRQ="y";
HARDWARE_WATCHPOINTS="y";
HAS_DMA="y";
HAS_IOMEM="y";
HAS_IOPORT_MAP="y";
I2C="y";
I2C_ALGOBIT="y";
I2C_BOARDINFO="y";
I2C_CHARDEV="y";
I2C_GPIO="y";
I2C_MT7621="y";
ICPLUS_PHY="y";
IRQCHIP="y";
IRQ_DOMAIN="y";
IRQ_DOMAIN_HIERARCHY="y";
IRQ_FORCED_THREADING="y";
IRQ_MIPS_CPU="y";
IRQ_WORK="y";
LIBFDT="y";
LOCK_DEBUGGING_SUPPORT="y";
LZO_COMPRESS="y";
LZO_DECOMPRESS="y";
MDIO_BUS="y";
MDIO_DEVICE="y";
MDIO_DEVRES="y";
MEDIATEK_GE_PHY="y";
MEMFD_CREATE="y";
MFD_SYSCON="y";
MIGRATION="y";
MIKROTIK="y";
MIKROTIK_RB_SYSFS="y";
MIPS="y";
MIPS_ASID_BITS="8";
MIPS_ASID_SHIFT="0";
MIPS_CLOCK_VSYSCALL="y";
MIPS_CM="y";
MIPS_CPC="y";
MIPS_CPS="y";
MIPS_CPU_SCACHE="y";
MIPS_GIC="y";
MIPS_L1_CACHE_SHIFT="5";
MIPS_LD_CAN_LINK_VDSO="y";
MIPS_MT="y";
MIPS_MT_FPAFF="y";
MIPS_MT_SMP="y";
MIPS_NR_CPU_NR_MAP="4";
MIPS_PERF_SHARED_TC_COUNTERS="y";
MIPS_SPRAM="y";
MODULES_USE_ELF_REL="y";
MTD_CMDLINE_PARTS="y";
MTD_NAND_CORE="y";
MTD_NAND_ECC="y";
MTD_NAND_ECC_SW_HAMMING="y";
MTD_NAND_MT7621="y";
MTD_NAND_MTK_BMT="y";
MTD_RAW_NAND="y";
MTD_ROUTERBOOT_PARTS="y";
MTD_SERCOMM_PARTS="y";
MTD_SPI_NOR="y";
MTD_SPLIT_FIT_FW="y";
MTD_SPLIT_MINOR_FW="y";
MTD_SPLIT_SEAMA_FW="y";
MTD_SPLIT_TPLINK_FW="y";
MTD_SPLIT_TRX_FW="y";
MTD_SPLIT_UIMAGE_FW="y";
MTD_UBI="y";
MTD_UBI_BEB_LIMIT="20";
MTD_UBI_BLOCK="y";
MTD_UBI_WL_THRESHOLD="4096";
MTD_VIRT_CONCAT="y";
NEED_DMA_MAP_STATE="y";
NET_DEVLINK="y";
NET_DSA="y";
NET_DSA_MT7530="y";
NET_DSA_MT7530_MDIO="y";
NET_DSA_TAG_MTK="y";
NET_FLOW_LIMIT="y";
NET_MEDIATEK_SOC="y";
NET_SELFTESTS="y";
NET_SWITCHDEV="y";
NET_VENDOR_MEDIATEK="y";
NO_HZ_COMMON="y";
NO_HZ_IDLE="y";
NR_CPUS="4";
NVMEM="y";
OF="y";
OF_ADDRESS="y";
OF_EARLY_FLATTREE="y";
OF_FLATTREE="y";
OF_GPIO="y";
OF_IRQ="y";
OF_KOBJ="y";
OF_MDIO="y";
PAGE_POOL="y";
PAGE_POOL_STATS="y";
PCI="y";
PCIE_MT7621="y";
PCI_DISABLE_COMMON_QUIRKS="y";
PCI_DOMAINS="y";
PCI_DOMAINS_GENERIC="y";
PCI_DRIVERS_GENERIC="y";
PCS_MTK_LYNXI="y";
PERF_USE_VMALLOC="y";
PGTABLE_LEVELS="2";
PHYLIB="y";
PHYLINK="y";
PHY_MT7621_PCI="y";
PINCTRL="y";
PINCTRL_AW9523="y";
PINCTRL_MT7621="y";
PINCTRL_RALINK="y";
PINCTRL_SX150X="y";
POWER_RESET="y";
POWER_RESET_GPIO="y";
POWER_SUPPLY="y";
PTP_1588_CLOCK_OPTIONAL="y";
QUEUED_RWLOCKS="y";
QUEUED_SPINLOCKS="y";
RALINK="y";
RATIONAL="y";
REGMAP="y";
REGMAP_I2C="y";
REGMAP_MMIO="y";
REGULATOR="y";
REGULATOR_FIXED_VOLTAGE="y";
RESET_CONTROLLER="y";
RFS_ACCEL="y";
RPS="y";
RTC_CLASS="y";
RTC_DRV_BQ32K="y";
RTC_DRV_PCF8563="y";
RTC_I2C_AND_SPI="y";
SCHED_SMT="y";
SERIAL_8250="y";
SERIAL_8250_CONSOLE="y";
SERIAL_8250_NR_UARTS="3";
SERIAL_8250_RUNTIME_UARTS="3";
SERIAL_MCTRL_GPIO="y";
SERIAL_OF_PLATFORM="y";
SGL_ALLOC="y";
SMP="y";
SMP_UP="y";
SOCK_RX_QUEUE_MAPPING="y";
SOC_BUS="y";
SOC_MT7621="y";
SPI="y";
SPI_MASTER="y";
SPI_MEM="y";
SPI_MT7621="y";
SRCU="y";
SWPHY="y";
SYNC_R4K="y";
SYSCTL_EXCEPTION_TRACE="y";
SYS_HAS_CPU_MIPS32_R1="y";
SYS_HAS_CPU_MIPS32_R2="y";
SYS_HAS_EARLY_PRINTK="y";
SYS_SUPPORTS_32BIT_KERNEL="y";
SYS_SUPPORTS_ARBIT_HZ="y";
SYS_SUPPORTS_HIGHMEM="y";
SYS_SUPPORTS_HOTPLUG_CPU="y";
SYS_SUPPORTS_LITTLE_ENDIAN="y";
SYS_SUPPORTS_MIPS16="y";
SYS_SUPPORTS_MIPS_CPS="y";
SYS_SUPPORTS_MULTITHREADING="y";
SYS_SUPPORTS_SCHED_SMT="y";
SYS_SUPPORTS_SMP="y";
SYS_SUPPORTS_ZBOOT="y";
TARGET_ISA_REV="2";
TICK_CPU_ACCOUNTING="y";
TIMER_OF="y";
TIMER_PROBE="y";
TREE_RCU="y";
TREE_SRCU="y";
UBIFS_FS="y";
USB_SUPPORT="y";
USE_OF="y";
WEAK_ORDERING="y";
XPS="y";
XXHASH="y";
ZLIB_DEFLATE="y";
ZLIB_INFLATE="y";
ZSTD_COMPRESS="y";
ZSTD_DECOMPRESS="y";
} // lib.optionalAttrs (config.system.service ? watchdog) {
RALINK_WDT = "y"; # watchdog
MT7621_WDT = "y"; # or it might be this one
};
conditionalConfig = {
WLAN = {
MT7915E = "m";
};
};
};
tplink-safeloader.board = "ARCHER-AX23-V1";
boot = {
commandLine = [ "console=ttyS0,115200" ];
tftp = {
# Should be a segment of free RAM, where the tftp artifact
# can be stored before unpacking it to the 'hardware.loadAddress'
# The 'hardware.loadAddress' is 0x80001000, which suggests the
# RAM would start at 0x8000000 and (being 128MB) go to
# to 0x8800000. Let's put it at the 100MB mark at
# 0x8000000+0x0640000=0x86400000
loadAddress = lim.parseInt "0x86400000";
};
};
filesystem =
let inherit (pkgs.pseudofile) dir symlink;
in
dir {
lib = dir {
firmware = dir {
mediatek = symlink firmware;
};
};
};
hardware =
let
openwrt = pkgs.openwrt;
mac80211 = pkgs.kmodloader.override {
targets = [
"mt7915e"
];
inherit (config.system.outputs) kernel;
};
in {
# from OEM bootlog (openwrt wiki):
# 4 cmdlinepart partitions found on MTD device raspi
# Creating 4 MTD partitions on "raspi":
# 0x000000000000-0x000000040000 : "uboot"
# 0x000000040000-0x000000440000 : "uImage"
# 0x000000440000-0x000000ff0000 : "rootfs"
# 0x000000ff0000-0x000001000000 : "ART"
# from openwrt bootlog (openwrt wiki):
# 5 fixed-partitions partitions found on MTD device spi0.0
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
# OF: Bad cell count for /palmbus@1e000000/spi@b00/flash@0/partitions
# Creating 5 MTD partitions on "spi0.0":
# 0x000000000000-0x000000040000 : "u-boot"
# 0x000000040000-0x000000fa0000 : "firmware"
# 2 uimage-fw partitions found on MTD device firmware
# Creating 2 MTD partitions on "firmware":
# 0x000000000000-0x0000002c0000 : "kernel"
# 0x0000002c0000-0x000000f60000 : "rootfs"
# mtd: setting mtd3 (rootfs) as root device
# 1 squashfs-split partitions found on MTD device rootfs
# 0x000000640000-0x000000f60000 : "rootfs_data"
# 0x000000fa0000-0x000000fb0000 : "config"
# 0x000000fb0000-0x000000ff0000 : "tplink"
# 0x000000ff0000-0x000001000000 : "radio"
flash = {
# from the OEM bootlog 'Booting image at bc040000'
# (0x40000 from 0xbc000000)
address = lim.parseInt "0xbc040000";
# 0x000000040000-0x000000fa0000
size = lim.parseInt "0xf60000";
# TODO: find in /proc/mtd on a running system
eraseBlockSize = 65536;
};
# since this is mentioned in the partition table as well?
defaultOutput = "tplink-safeloader";
# taken from openwrt sysupgrade image:
# openwrt-23.05.2-ramips-mt7621-tplink_archer-ax23-v1-squashfs-sysupgrade.bin: u-boot legacy uImage, MIPS OpenWrt Linux-5.15.137, Linux/MIPS, OS Kernel Image (lzma), 2797386 bytes, Tue Nov 14 13:38:11 2023, Load Address: 0X80001000, Entry Point: 0X80001000, Header CRC: 0X19F74C5B, Data CRC: 0XF685563C
loadAddress = lim.parseInt "0x80001000";
entryPoint = lim.parseInt "0x80001000";
rootDevice = "/dev/mtdblock3";
dts = {
src = "${openwrt.src}/target/linux/ramips/dts/mt7621_tplink_archer-ax23-v1.dts";
includePaths = [
"${openwrt.src}/target/linux/ramips/dts"
"${config.system.outputs.kernel.modulesupport}/arch/arm64/boot/dts/mediatek/"
];
};
networkInterfaces =
let
inherit (config.system.service.network) link;
in rec {
lan1 = link.build { ifname = "lan1"; };
lan2 = link.build { ifname = "lan2"; };
lan3 = link.build { ifname = "lan3"; };
lan4 = link.build { ifname = "lan4"; };
wan = link.build { ifname = "wan"; };
wlan = link.build {
ifname = "wlan0";
dependencies = [ mac80211 ];
};
wlan5 = link.build {
ifname = "wlan1";
dependencies = [ mac80211 ];
};
};
};
};
};
}

View File

@ -2,6 +2,149 @@
description = ''
Turris Omnia
************
This is a 32 bit ARMv7 MVEBU device, which is usually shipped with
TurrisOS, an OpenWrt-based system. Rather than reformatting the
builtin storage, we install Liminix on to the existing btrfs
filesystem so that the vendor snapshot/recovery system continues
to work (and provides you an easy rollback if you decide you don't
like Liminix after all).
The install process has two stages, and is intended that you
should not need to open the device and add a serial console
(although it may be handy for visibility, and in case anything
goes wrong). First we build a minimal installation/recovery
system, then we reboot into that recovery image to prepare the
device for the full target install.
Installation using a USB stick
==============================
First, build the image for the USB stick. Review
:file:`examples/recovery.nix` in order to change the default
root password (which is ``secret``) and/or the SSH keys, then
build it with
.. code-block:: console
$ nix-build -I liminix-config=./examples/recovery.nix \
--arg device "import ./devices/turris-omnia" \
-A outputs.mbrimage -o mbrimage
$ file -L mbrimage
mbrimage: DOS/MBR boot sector; partition 1 : ID=0x83, active, start-CHS (0x0,0,5), end-CHS (0x6,130,26), startsector 4, 104602 sectors
Next, copy the image from your build machine to a USB storage
medium using :command:`dd` or your other most favoured file copying
tool, which might be a comand something like this:
.. code-block:: console
$ dd if=mbrimage of=/dev/path/to/the/usb/stick \
bs=1M conv=fdatasync status=progress
The Omnia's default boot order only checks USB after it has failed
to boot from eMMC, which is not ideal for our purpose. Unless you
have a serial cable, the easiest way to change this is by booting
to TurrisOS and logging in with ssh:
.. code-block:: console
root@turris:/# fw_printenv boot_targets
boot_targets=mmc0 nvme0 scsi0 usb0 pxe dhcp
root@turris:/# fw_setenv boot_targets usb0 mmc0
root@turris:/# fw_printenv boot_targets
boot_targets=usb0 mmc0
root@turris:/# reboot -f
It should now boot into the recovery image. It expects a network
cable to be plugged into LAN2 with something on the other end of
it that serves DHCP requests. Check your DHCP server logs for a
request from a ``liminix-recovery`` host and figure out what IP
address was assigned.
.. code-block:: console
$ ssh liminix-recovery.lan
You should get a "Busybox" banner and a root prompt. Now you can
start preparing the device to install Liminix on it. First we'll
mount the root filesystem and take a snapshot:
.. code-block:: console
# mkdir /dest && mount /dev/mmcblk0p1 /dest
# schnapps -d /dest create "pre liminix"
# schnapps -d /dest list
ERROR: not a valid btrfs filesystem: /
# | Type | Size | Date | Description
------+-----------+-------------+---------------------------+------------------------------------
1 | single | 16.00KiB | 1970-01-01 00:11:49 +0000 | pre liminix
(``not a valid btrfs filesystem: /`` is not a real error)
then we can remove all the files
.. code-block:: console
# rm -r /dest/@/*
and then it's ready to install the real Liminix system onto. On
your build system, create the Liminix configuration you wish to
install: here we'll use the ``rotuer`` example.
.. code-block:: console
build$ nix-build -I liminix-config=./examples/rotuer.nix \
--arg device "import ./devices/turris-omnia" \
-A outputs.systemConfiguration
and then use :command:`min-copy-closure` to copy it to the device.
.. code-block:: console
build$ nix-shell --run \
"min-copy-closure -r /dest/@ root@liminix-recovery.lan result"
and activate it
.. code-block:: console
build$ ssh root@liminix-recovery.lan \
"/dest/@/$(readlink result)/bin/install /dest/@"
The final steps are performed directly on the device again: add
a symlink so U-Boot can find :file:`/boot`, then restore the
default boot order and reboot into the new configuration.
.. code-block:: console
# cd /dest && ln -s @/boot .
# fw_setenv boot_targets "mmc0 nvme0 scsi0 usb0 pxe dhcp"
# cd / ; umount /dest
# reboot
Installation using a TFTP server and serial console
===================================================
If you have a :ref:`serial` console connection and a TFTP server,
and would rather use them than fiddling with USB sticks, the
:file:`examples/recovery.nix` configuration also works
using the ``tftpboot`` output. So you can do
.. code-block:: console
build$ nix-build -I liminix-config=./examples/recovery.nix \
--arg device "import ./devices/turris-omnia" \
-A outputs.tftpboot
and then paste the generated :file:`result/boot.scr` into
U-Boot, and you will end up with the same system as you would
have had after booting from USB. If you don't have a serial
console connection you could probably even get clever with
elaborate use of :command:`fw_setenv`, but that is left as
an exercise for the reader.
'';
system = {
@ -12,8 +155,6 @@
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 {
@ -30,9 +171,7 @@
imports = [
../../modules/arch/arm.nix
../../modules/outputs/tftpboot.nix
../../modules/outputs/ext4fs.nix
../../modules/outputs/mbrimage.nix
../../modules/outputs/extlinux.nix
];
config = {
@ -40,12 +179,10 @@
kernel = {
src = pkgs.pkgsBuildBuild.fetchurl {
name = "linux.tar.gz";
url = "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.71.tar.gz";
hash = "sha256-yhO2cXIeIgUxkSZf/4aAsF11uxyh+UUZu6D1h92vCD8=";
url = "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.7.4.tar.gz";
hash = "sha256-wIrmL0BS63nRwWfm4nw+dRNVPUzGh9M4X7LaHzAn5tU=";
};
extraPatchPhase = ''
${pkgs.openwrt.applyPatches.mvebu}
'';
version = "6.7.4";
config = {
PCI = "y";
OF = "y";
@ -57,11 +194,27 @@
ARCH_MULTI_V7= "y";
PCI_MVEBU = "y";
AHCI_MVEBU = "y";
RTC_CLASS = "y";
RTC_DRV_ARMADA38X = "y"; # this may be useful anyway?
EXPERT = "y";
ALLOW_DEV_COREDUMP = "n";
# dts has a compatible for this but dmesg is not
# showing it
EEPROM_AT24 = "y"; # atmel,24c64
I2C = "y";
I2C_MUX = "y";
I2C_MUX_PCA954x = "y";
MACH_ARMADA_38X = "y";
SMP = "y";
# this is disabled for the moment because it relies on a GCC
# plugin that requires gmp.h to build, and I can't see right now
# how to confgure it to find gmp
# this is disabled for the moment because it relies on a
# GCC plugin that requires gmp.h to build, and I can't see
# right now how to confgure it to find gmp
STACKPROTECTOR_PER_TASK = "n";
NR_CPUS = "4";
VFP = "y";
@ -70,11 +223,6 @@
# WARNING: unmet direct dependencies detected for ARCH_WANT_LIBATA_LEDS
ATA = "y";
PSTORE = "y";
PSTORE_RAM = "y";
PSTORE_CONSOLE = "y";
PSTORE_DEFLATE_COMPRESS = "n";
BLOCK = "y";
MMC="y";
PWRSEQ_EMMC="y"; # ???
@ -110,6 +258,7 @@
MVNETA_BM_ENABLE = "y";
SRAM = "y"; # mmio-sram is "compatible" for bm_bppi reqd by BM
PHY_MVEBU_A38X_COMPHY = "y"; # for eth2
MARVELL_PHY = "y";
MVPP2 = "y";
MV_XOR = "y";
@ -126,14 +275,27 @@
NET_DSA = "y";
NET_DSA_MV88E6XXX = "y"; # depends on PTP_1588_CLOCK_OPTIONAL
};
conditionalConfig = {
USB = {
USB_XHCI_MVEBU = "y";
USB_XHCI_HCD = "y";
};
WLAN = {
WLAN_VENDOR_ATH = "y";
ATH_COMMON = "m";
ATH9K = "m";
ATH9K_PCI = "y";
ATH10K = "m";
ATH10K_PCI = "m";
ATH10K_DEBUG = "y";
};
};
};
rootfsType = "ext4";
boot = {
commandLine = [
"console=ttyS0,115200"
"pcie_aspm=off" # ath9k pci incompatible with PCIe ASPM
];
imageFormat = "fit";
};
filesystem =
let
@ -142,9 +304,9 @@
name = "wlan-firmware";
phases = ["installPhase"];
installPhase = ''
mkdir $out
cp -r ${pkgs.linux-firmware}/lib/firmware/ath10k/QCA988X $out
'';
mkdir $out
cp -r ${pkgs.linux-firmware}/lib/firmware/ath10k/QCA988X $out
'';
};
in dir {
lib = dir {
@ -162,33 +324,32 @@
};
boot.tftp = {
loadAddress = lim.parseInt "0x1000000";
loadAddress = lim.parseInt "0x1700000";
kernelFormat = "zimage";
compressRoot = true;
};
hardware = let
mac80211 = pkgs.mac80211.override {
drivers = ["ath9k_pci" "ath10k_pci"];
klibBuild = config.system.outputs.kernel.modulesupport;
mac80211 = pkgs.kmodloader.override {
inherit (config.system.outputs) kernel;
targets = ["ath9k" "ath10k_pci"];
};
in {
defaultOutput = "mtdimage";
defaultOutput = "updater";
loadAddress = lim.parseInt "0x00800000"; # "0x00008000";
entryPoint = lim.parseInt "0x00800000"; # "0x00008000";
rootDevice = "/dev/mtdblock0";
rootDevice = "/dev/mmcblk0p1";
dts = {
src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/armada-385-turris-omnia.dts";
includes = [
"${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/"
src = "${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/armada-385-turris-omnia.dts";
includePaths = [
"${config.system.outputs.kernel.modulesupport}/arch/arm/boot/dts/marvell/"
];
};
flash.eraseBlockSize = 65536; # only used for tftpboot
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.
@ -210,7 +371,7 @@
# per
# https://www.kernel.org/doc/html/latest/networking/dsa/configuration.html#affinity-of-user-ports-to-cpu-ports
# but apparently OpenWrt doesn't either so maybe it's more
# complicated than it looks
# complicated than it looks.
wan = link.build {
# in armada-38x.dtsi this is eth2. It may be connected to
@ -219,9 +380,13 @@
ifname = "wan";
};
lan = link.build {
ifname = "lan1";
};
lan0 = link.build { ifname = "lan0"; };
lan1 = link.build { ifname = "lan1"; };
lan2 = link.build { ifname = "lan2"; };
lan3 = link.build { ifname = "lan3"; };
lan4 = link.build { ifname = "lan4"; };
lan5 = link.build { ifname = "lan5"; };
lan = lan0; # maybe we should build a bridge?
wlan = link.build {
ifname = "wlan0";

View File

@ -0,0 +1,155 @@
#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";
};
};

View File

@ -0,0 +1,155 @@
#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";
};
};

View File

@ -0,0 +1,359 @@
{
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;
includePaths = [
# 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 = {
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
};
};
};
}

View File

@ -0,0 +1,56 @@
#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";
};
};

View File

@ -12,9 +12,13 @@ BUILDDIR = _build
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
hardware.rst: hardware.nix
@rm -f hardware.rst || true
@cp $$(nix-build hardware.nix) hardware.rst
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
html: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@ -4,181 +4,192 @@ System Administration
Services on a running system
****************************
* add an s6-rc cheatsheet here
Liminix services are built on s6-rc, which is itself layered on s6.
Services are defined at build time in your configuration (see
:ref:`configuration-services` for information) and can't be added
to/changed at runtime, but to monitor
events or diagnose problems you may need to inspect them on the
running system. Here are some of the most commonly used s6,-rc
commands:
.. list-table:: Service management quick reference
:widths: 55 45
:header-rows: 1
* - What
- How
* - List all running services
- ``s6-rc -a list``
* - List all services that are **not** running
- ``s6-rc -da list``
* - List services that ``wombat`` depends on
- ``s6-rc-db dependencies wombat``
* - ... transitively
- ``s6-rc-db all-dependencies wombat``
* - List services that depend on service ``wombat``
- ``s6-rc-db -d dependencies wombat``
* - ... transitively
- ``s6-rc-db -d all-dependencies wombat``
* - Stop service ``wombat`` and everything depending on it
- ``s6-rc -d change wombat``
* - Start service ``wombat`` (but not any services depending on it)
- ``s6-rc -u change wombat``
* - Start service ``wombat`` and all* services depending on it
- ``s6-rc-up-tree wombat``
Flashing and updating
*********************
:command:`s6-rc-up-tree` brings up a service and all services that
depend on it, except for any services that depend on a "controlled"
service that is not currently running. Controlled services are not
started at boot time but in response to external events (e.g. plugging
in a particular piece of hardware) so you probably don't want to be
starting them by hand if the conditions aren't there.
A service may be **up** or **down** (there are no intermediate states
like "started" or "stopping" or "dying" or "cogitating"). Some (but
not all) services have "readiness" notifications: the dependents of a
service with a readiness notification won't be started until the
service signals (by writing to a nominated file descriptor) that it's
prepared to start work. Most services defined by Liminix also have a
``timeout-up`` parameter, which means that if a service has readiness
notifications and doesn't become ready in the allotted time (defaults
20 seconds) it will be terminated and its state set to **down**.
If the process providing a service dies, it will be restarted
automatically. Liminix does not automatically set it to **down**.
Flashing from Liminix
=====================
(If the process providing a service dies without ever notifying
readiness, Liminix will restart it as many times as it has to until the
timeout period elapses, and then stop it and mark it down.)
The flash procedure from an existing Liminix-system has two steps.
First we reboot the device (using "kexec") into an "ephemeral"
RAM-based version of the new configuration, then when we're happy it
works we can flash the image - and if it doesn't work we can reboot
the device again and it will boot from the old image.
Controlled services
===================
**Controlled** services are those which are started/stopped on demand
by a **controller** (another service) instead of being started at boot
time. For example:
Building the RAM-based image
----------------------------
* ``svc.uevent-rule.build`` creates a controlled service which is
active when a particular hardware device (identified by uevent/sysfs
directory) is present.
To create the ephemeral image, build ``outputs.kexecboot`` instead of
``outputs.default``. This generates a directory containing the root
filesystem image and kernel, along with an executable called `kexec`
and a `boot.sh` script that runs it with appropriate arguments.
* ``svc.round-robin.build`` creates a service controller that
invokes two or more services in turn, running the next one when the
process providing the previous one exits. We use this for failover
from one network connection to a backup connection, for example.
For example
* ``svc.health-check.build`` creates a service controller that
runs a controlled service and periodically tests whether it is
healthy by running an external health check command or script. If the
check command repeatedly fails, the controlled service is
restarted.
The Configuration section of the manual describes controlled
services in more detail. Some operational considerations
* ``round-robin`` detects a service status by looking at its
:file:`outputs` directory, so it won't work unless the service
creates some outputs. This is considered a bug and will be
fixed in a future release
* ``health-check`` works for longruns but not for oneshots, as it
internally relies on ``s6-svc`` to restart the process
Logs
====
Logs for all services are collated into :file:`/run/log/current`.
The log file is rotated when it reaches a threshold size, into another
file in the same directory whose name contains a TAI64 timestamp.
Each log line is prefixed with a TAI64 timestamp and the name of the
service, if it is a longrun. If it is a oneshot, a timestamp and the
name of some other service. To convert the timestamp into a
human-readable format, use :command:`s6-tai64nlocal`.
.. code-block:: console
nix-build -I liminix-config=./examples/arhcive.nix \
--arg device "import ./devices/gl-ar750"
-A outputs.kexecboot && \
(tar chf - result | ssh root@the-device tar -C /run -xvf -)
# ls -l /run/log/
-rw-r--r-- 1 0 lock
-rw-r--r-- 1 0 state
-rwxr--r-- 1 98059 @4000000000025cb629c311ac.s
-rwxr--r-- 1 98061 @40000000000260f7309c7fb4.s
-rwxr--r-- 1 98041 @40000000000265233a6cc0b6.s
-rwxr--r-- 1 98019 @400000000002695d10c06929.s
-rwxr--r-- 1 98064 @4000000000026d84189559e0.s
-rwxr--r-- 1 98055 @40000000000271ce1e031d91.s
-rwxr--r-- 1 98054 @400000000002760229733626.s
-rwxr--r-- 1 98104 @4000000000027a2e3b6f4e12.s
-rwxr--r-- 1 98023 @4000000000027e6f0ed24a6c.s
-rw-r--r-- 1 42374 current
# tail -2 /run/log/current
@40000000000284f130747343 wan.link.pppoe Connect: ppp0 <--> /dev/pts/0
@40000000000284f230acc669 wan.link.pppoe sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x667a9594> <pcomp> <accomp>]
# tail -2 /run/log/current | s6-tai64nlocal
1970-01-02 21:51:45.828598156 wan.link.pppoe sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x667a9594> <pcomp> <accom
p>]
1970-01-02 21:51:48.832588765 wan.link.pppoe sent [LCP ConfReq id=0x1 <asyncmap 0x0> <magic 0x667a9594> <pcomp> <accom
p>]
and then login to the device and run
Log persistence
---------------
Logs written to :file:`/run/log/` will not survive a reboot or crash,
as it is an ephemeral filesystem.
On supported hardware you can enable logging to `pstore
<https://www.kernel.org/doc/Documentation/ABI/testing/pstore>`_ which
means the most recent log messages will be preserved on reboot. Set
the config option ``logging.persistent.enable = true`` to log messages
to :file:`/dev/pmsg0` as well as to the regular log. This is a
circular buffer, so when it fills up newer messages will overwrite the
oldest messages.
To check the previous messages after a (planned or forced) reboot,
you need to mooun the pstore filesystem.
.. code-block:: console
cd /run/result
sh ./boot.sh .
# mount -t pstore pstore /sys/fs/pstore/
# ls -l /sys/fs/pstore/
-r--r--r-- 1 43071 pmsg-ramoops-0
# cat /sys/fs/pstore/pmsg-ramoops-0
@40000000000000282c997d29 mydevice klogd <6>[ 30.793756] int: port 2(wlan0) entered blocking state
[log messages from before the reboot follow]
This will load the new kernel and map the root filesystem into a RAM
disk, then start executing the new kernel. *This is effectively a
reboot - be sure to close all open files and finish anything else
you were doing first.*
If the new system crashes or is rebooted, then the device will revert
to the old configuration it finds in flash.
Updating an installed system
****************************
If your system has a writable root filesystem (JFFS2, btrfs etc -
anything but squashfs), we have mechanisms for in-places updates
analogous to :command:`nixos-rebuild`, but the operation is a bit
different because it expects to run on a build machine and then copy
to the host device using :command:`ssh`.
Building the second (permanent) image
-------------------------------------
While running in the kexecboot system, you can build the permanent
image and copy it to the device with :command:`ssh`
.. code-block:: console
build-machine$ nix-build -I liminix-config=./examples/arhcive.nix \
--arg device "import ./devices/gl-ar750"
-A outputs.default && \
(tar chf - result | ssh root@the-device tar -C /run -xvf -)
build-machine$ tar chf - result/firmware.bin | \
ssh root@the-device tar -C /run -xvf -
Next you need to connect to the device and locate the "firmware"
partition, which you can do with a combination of :command:`dmesg`
output and the contents of :file:`/proc/mtd`
.. code-block:: console
<5>[ 0.469841] Creating 4 MTD partitions on "spi0.0":
<5>[ 0.474837] 0x000000000000-0x000000040000 : "u-boot"
<5>[ 0.480796] 0x000000040000-0x000000050000 : "u-boot-env"
<5>[ 0.487056] 0x000000050000-0x000000060000 : "art"
<5>[ 0.492753] 0x000000060000-0x000001000000 : "firmware"
# cat /proc/mtd
dev: size erasesize name
mtd0: 00040000 00001000 "u-boot"
mtd1: 00010000 00001000 "u-boot-env"
mtd2: 00010000 00001000 "art"
mtd3: 00fa0000 00001000 "firmware"
mtd4: 002a0000 00001000 "kernel"
mtd5: 00d00000 00001000 "rootfs"
Now run (in this example)
.. code-block:: console
flashcp -v firmware.bin /dev/mtd3
"I know my new image is good, can I skip the intermediate step?"
----------------------------------------------------------------
In addition to giving you a chance to see if the new image works, this
two-step process ensures that you're not copying the new image over
the top of the active root filesystem. Sometimes it works, but you
will at least need physical access to the device to power-cycle it
because it will be effectively frozen afterwards.
Flashing from the boot monitor
==============================
If you are prepared to open the device and have a TTL serial adaptor
of some kind to connect it to, you can probably use U-Boot and a TFTP
server to download and flash the image. This is quite
hardware-specific, and sometimes involves soldering: please refer
to :ref:`serial`.
Flashing from OpenWrt
=====================
.. CAUTION:: Untested! A previous version of these instructions
(without the -e flag) led to bricking the device
when flashing a jffs2 image. If you are reading
this message, nobody has yet reported on whether the
new instructions are any better.
If your device is running OpenWrt then it probably has the
:command:`mtd` command installed. After transferring the image onto the
device using e.g. :command:`ssh`, you can run it as follows:
.. code-block:: console
mtd -e -r write /tmp/firmware.bin firmware
The options to this command are for "erase before writing" and "reboot
after writing".
For more information, please see the `OpenWrt manual <https://openwrt.org/docs/guide-user/installation/sysupgrade.cli>`_ which may also contain (hardware-dependent) instructions on how to flash an image using the vendor firmware - perhaps even from a web interface.
Updating an installed system (JFFS2)
************************************
Adding packages
===============
If your device is running a JFFS2 root filesystem, you can build
extra packages for it on your build system and copy them to the
device: any package in Nixpkgs or in the Liminix overlay is available
with the ``pkgs`` prefix:
To use this, build the ``outputs.updater``
target and then run the :command:`update.sh` script it generates.
.. code-block:: console
nix-build -I liminix-config=./my-configuration.nix \
--arg device "import ./devices/mydevice" -A pkgs.tcpdump
--arg device "import ./devices/mydevice" \
-A outputs.updater
./result/bin/update.sh root@the-device
nix-shell -p min-copy-closure root@the-device result/
The update script uses min-copy-closure to copy new or changed
packages to the device, then (perhaps) reboots it. The reboot
behaviour can be affected by flags:
Note that this only copies the package to the device: it doesn't update
any profile to add it to ``$PATH``
* `--no-reboot` will cause it not to reboot at all, if you would
rather do that yourself. Note that none of the newly-installed or
updated services will be running until you do.
Rebuilding the system
=====================
:command:`liminix-rebuild` is the Liminix analogue of :command:`nixos-rebuild`, although its operation is a bit different because it expects to run on a build machine and then copy to the host device. Run it with the same ``liminix-config`` and ``device`` parameters as you would run :command:`nix-build`, and it will build any new/changed packages and then copy them to the device using SSH. For example:
.. code-block:: console
liminix-rebuild root@the-device -I liminix-config=./examples/rotuer.nix --arg device "import ./devices/gl-ar750"
This will
* build anything that needs building
* copy new or changed packages to the device
* reboot the device
* `--fast` causes it tn not do a full reboot, but instead to restart
only the services that have been changed. This will restart all of
the services that have updated store paths (and anything that
depends on them), but will not affect services that haven't changed.
It doesn't delete old packages automatically: to do that run
:command:`min-collect-garbage`, which will delete any packages not in
@ -196,3 +207,136 @@ Caveats
nixpkgs).
* it cannot upgrade the kernel, only userland
.. _levitate:
Adding packages
===============
If you simply wish to add a package without any change to services,
you can call :command:`min-copy-closure` directly to install
any package in Nixpkgs or in the Liminix overlay
.. code-block:: console
nix-build -I liminix-config=./my-configuration.nix \
--arg device "import ./devices/mydevice" -A pkgs.tcpdump
nix-shell -p min-copy-closure root@the-device result/
Note that this only copies the package and its dependencies to the
device: it doesn't update any profile to add it to ``$PATH``
.. _rebuilding the system:
Reinstalling on a running system
********************************
Liminix is initially installed from a monolithic
:file:`firmware.bin` - and unless you're running a writable
filesystem, the only way to update it is to build and install a whole
new :file:`firmware.bin`. However, you probably would prefer not to
have to remove it from its installation site, unplug it from the
network and stick serial cables in it all over again.
It is not (generally) safe to install a new firmware onto the flash
partitions that the active system is running on. To address this we
have :command:`levitate`, which a way for a running Liminix system to
"soft restart" into a ramdisk running only a limited set of services,
so that the main partitions can then be safely flashed.
Configuration
=============
Levitate *needs to be configured when you create the initial system*
to specify which services/packages/etc to run in maintenance
mode. Most likely you want to configure a network interface and an ssh
for example so that you can login to reflash it.
.. code-block:: nix
defaultProfile.packages = with pkgs; [
...
(levitate.override {
config = {
services = {
inherit (config.services) dhcpc sshd watchdog;
};
defaultProfile.packages = [ mtdutils ];
users.root = config.users.root;
};
})
];
Use
===
Connect (with ssh, probably) to the running Liminix system that you
wish to upgrade.
.. code-block:: console
bash$ ssh root@the-device
Run :command:`levitate`. This takes a little while (perhaps a few
tens of seconds) to execute, and copies all config required for
maintenance mode to :file:`/run/maintenance`.
.. code-block:: console
# levitate
Reboot into maintenance mode. You will be logged out
.. code-block:: console
# reboot
Connect to the device again - note that the ssh host key will have changed.
.. code-block:: console
# ssh -o UserKnownHostsFile=/dev/null root@the-device
Check we're in maintenance mode
.. code-block:: console
# cat /etc/banner
LADIES AND GENTLEMEN WE ARE FLOATING IN SPACE
Most services are disabled. The system is operating
with a ram-based root filesystem, making it safe to
overwrite the flash devices in order to perform
upgrades and maintenance.
Don't forget to reboot when you have finished.
Perform the upgrade, using flashcp. This is an example,
your device will differ
.. code-block:: console
# cat /proc/mtd
dev: size erasesize name
mtd0: 00030000 00010000 "u-boot"
mtd1: 00010000 00010000 "u-boot-env"
mtd2: 00010000 00010000 "factory"
mtd3: 00f80000 00010000 "firmware"
mtd4: 00220000 00010000 "kernel"
mtd5: 00d60000 00010000 "rootfs"
mtd6: 00010000 00010000 "art"
# flashcp -v firmware.bin mtd:firmware
All done
.. code-block:: console
# reboot

View File

@ -7,19 +7,19 @@
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'Liminix'
copyright = '2023, Daniel Barlow'
copyright = '2023-2024 Daniel Barlow'
author = 'Daniel Barlow'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = [
'sphinx.ext.autosectionlabel'
# 'sphinx.ext.autosectionlabel'
]
autosectionlabel_prefix_document = True
templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ['*.inc.rst', '_build', 'Thumbs.db', '.DS_Store']

View File

@ -1,27 +1,8 @@
.. _configuration:
Configuration
#############
Liminix uses the Nix language to provide congruent configuration
management. This means that to change anything about the way in
which a Liminix system works, you make that change in
your :file:`configuration.nix` (or one of the other files it references),
and rerun :command:`nix-build` or :command:`liminix-rebuild` to action
the change. It is not possible (at least, without shenanigans) to make
changes by logging into the device and running imperative commands
whose effects may later be overridden: :file:`configuration.nix`
always describes the entire system and can be used to recreate that
system at any time. You can usefully keep it under version control.
If you are familiar with NixOS, you will notice some similarities
between NixOS and Liminix configuration, and also some
differences. Sometimes the differences are due to the
resource-constrained devices we deploy onto, sometimes due to
differences in the uses these devices are put to.
Configuration taxonomy
**********************
There are many things you can specify in a configuration, but these
are the ones you most commonly need to change:
@ -86,14 +67,166 @@ domains, or you want to run two SSH daemons on different ports.
don't use the NixOS modules themselves, because the
underlying system is not similar enough for them to work.
.. _configuration-services:
Services
********
We use the `s6-rc service manager <https://www.skarnet.org/software/s6-rc/overview.html>`_ to start/stop/restart services and handle
service dependencies. Any attribute in `config.services` will become
part of the default set of services that s6-rc will try to bring up on
boot.
In Liminix a service is any kind of long-running task or process on
the system, that is managed (started, stopped, and monitored) by a
service supervisor. A typical SOHO router might have services to
* answer DHCP and DNS requests from the LAN
* provide a wireless access point
* connect using PPPoE or L2TP to an upstream network
* start/stop the firewall
* enable/disable IP packet forwarding
* mount filesystems
(Some of these might not be considered services using other
definitions of the term: for example, this L2TP process would be a
"client" in the client/server classification; and enabling packet
forwarding doesn't require any long-lived process - just a setting to
be toggled. However, there is value in being able to use the same
abstractions for all the things to manage them and specify their
dependency relationships - so in Liminix "everything is a service")
The service supervision system enables service health monitoring,
restart of unhealthy services, and failover to "backup" services when
a primary service fails or its dependencies are unavailable. The
intention is that you have a framework in which you can specify policy
requirements like "ethernet wan dhcp-client should be restarted if it
crashes, but if it can't start because the hardware link is down, then
4G ppp service should be started instead".
Any attribute in `config.services` will become part of the default set
of services that s6-rc will try to bring up. Services are usually
started at boot time, but **controlled services** are those that are
required only in particular contexts. For example, a service to mount
a USB backup drive should run only when the drive is attached to the
system. Liminix currently implements three kinds of controlled service:
* "uevent-rule" service controllers use sysfs/uevent to identify when
particular hardware devices are present, and start/stop a controlled
service appropriately.
* the "round-robin" service controller is used for service failover:
it allows you to specify a list of services and runs each of them
in turn until it exits, then runs the next.
* the "health-check" service wraps another service, and runs a "health
check" command at regular intervals. When the health check fails,
indicating that the wrapped service is not working, it is terminated
and allowed to restart.
Runtime secrets (external vault)
================================
Secrets (such as wifi passphrases, PPP username/password, SSH keys,
etc) that you provide as literal values in :file:`configuration.nix`
are processed into into config files and scripts at build time, and
eventually end up in various files in the (world-readable)
:file:`/nix/store` before being baked into a flashable image. To
change a secret - whether due to a compromise, or just as part of to a
routine key rotation - you need to rebuild the configuration and
potentially reflash the affected devices.
To avoid this, you may instead use a "secrets service", which is a
mechanism for your device to fetch secrets from a source external to
the Nix store, and create at runtime the configuration files and
scripts that start the services which require them.
Not every possible parameter to every possible service is configurable
using a secrets service. Parameters which can be configured this way
are those with the type ``liminix.lib.types.replacable``. At the time
this document was written, these include:
* ppp (pppoe and l2tp): ``username``, ``password``
* ssh: ``authorizedKeys``
* hostapd: all parameters (most likely to be useful for ``wpa_passphrase``)
To use a runtime secret for any of these parameters:
* create a secrets service to specify the source of truth for secrets
* use the :code:`outputRef` function in the service parameter to specify the secrets service and path
For example, given you had an HTTPS server hosting a JSON file with the structure
.. code-block:: json
"ssh": {
"authorizedKeys": {
"root": [ "ssh-rsa ....", "ssh-rsa ....", ... ]
"guest": [ "ssh-rsa ....", "ssh-rsa ....", ... ]
}
}
you could use a :file:`configuration.nix` fragment something like this
to make those keys visible to ssh:
.. code-block:: nix
services.secrets = svc.secrets.outboard.build {
name = "secret-service";
url = "http://10.0.0.1/secrets.json";
username = "secrets";
password = "liminix";
interval = 30; # minutes
dependencies = [ config.services.lan ];
};
services.sshd = svc.ssh.build {
authorizedKeys = outputRef config.services.secrets "ssh/authorizedKeys";
};
There are presently two implementations of a secrets service:
Outboard secrets (HTTPS)
------------------------
This service expects a URL to a JSON file containing all the secrets.
You may specify a username and password along with the URL, which are
used if the file is password-protected (HTTP Basic
authentication). Note that this is not a protection against a
malicious local user: the username and password are normal build-time
parameters so will be readable in the Nix store. This is a mitigation
against the URL being accidentally discovered due to e.g. a log file
or error message on the server leaking.
Tang secrets (encrypted local file)
-----------------------------------
Aternatively, secrets may be stored locally on the device, in a file
that has been encrypted using `Tang <https://github.com/latchset/tang>`_.
Tang is a server for binding data to network presence.
This sounds fancy, but the concept is simple. You have some data, but you only want it to be available when the system containing the data is on a certain, usually secure, network.
.. code-block:: nix
services.secrets = svc.secrets.tang.build {
name = "secret-service";
path = "/run/mnt/usbstick/secrets.json.jwe";
interval = 30; # minutes
dependencies = [ config.services.mount-usbstick ];
};
The encryption uses the
same scheme/algorithm as `Clevis <https://github.com/latchset/clevis>`_ : you may use the `Clevis instructions <https://github.com/latchset/clevis?tab=readme-ov-file#pin-tang>`_ to
encrypt the file on another host and then copy it to your Liminix
device, or you can use :command:`tangc encrypt` to encrypt directly on
the device. (That latter approach may pose a chicken/egg problem if
the device needs secrets to boot up and run the services you are
relying on in order to login).
Writing services
================
For the most part, for common use cases, hopefully the services you
need will be defined by modules and you will only have to pass the
@ -141,11 +274,101 @@ Services may have dependencies: as you see above in the ``cowsayd``
example, it depends on some service called ``config.services.lan``,
meaning that it won't be started until that other service is up.
..
TODO: explain service outputs
Service outputs
===============
Outputs are a mechanism by which a service can provide data which may
be required by other services. For example:
* the DHCP client service can expect to receive nameserver address
information as one of the fields in the response from the DHCP
server: we provide that as an output which a dependent service for a
stub name resolver can use to configure its upstream servers.
* a service that creates a new network interface (e.g. ppp) will
provide the name of the interface (:code:`ppp0`, or :code:`ppp1` or
:code:`ppp7`) as an output so that a dependent service can reference
it to set up a route, or to configure firewall rules.
A service :code:`myservice` should write its outputs as files in
:file:`/run/services/outputs/myservice`: you can look around this
directory on a running Liminix system to see how it's used currently.
Usually we use the :code:`in_outputs` shell function in the
:command:`up` or :command:`run` attributes of the service:
.. code-block:: shell
(in_outputs ${name}
for i in lease mask ip router siaddr dns serverid subnet opt53 interface ; do
(printenv $i || true) > $i
done)
The outputs are just files, so technically you can read them using
anything that can read a file. Liminix has two "preferred"
mechanisms, though:
One-off lookups
---------------
In any context that ends up being evaluated by the shell, use
:code:`output` to print the value of an output
.. code-block:: nix
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.wan} address)";
target = "default";
dependencies = [ services.wan ];
};
Continuous updates
------------------
The downside of using shell functions in downstream service startup
scripts is that they only run when the service starts up: if a service
output *changes*, the downstream service would have to be restarted to
notice the change. Sometimes this is OK but other times the downstream
has no other need to restart, if it can only get its new data.
For this case, there is the :code:`anoia.svc` Fennel library, which
allows you to write a simple loop which is iterated over whenever a
service's outputs change. This code is from
:file:`modules/dhcp6c/acquire-wan-address.fnl`
.. code-block:: fennel
(fn update-addresses [wan-device addresses new-addresses exec]
;; run some appropriate "ip address [add|remove]" commands
)
(fn run []
(let [[state-directory wan-device] arg
dir (svc.open state-directory)]
(accumulate [addresses []
v (dir:events)]
(update-addresses wan-device addresses
(or (v:output "address") []) system))))
The :code:`output` method seen here accepts a filename (relative
to the service's output directory), or a directory name. It
returns the first line of that file, or for directories it
returns a table (Lua's key/value datastructure, similar to
a hash/dictionary) of the outputs in that directory.
Output design considerations
----------------------------
For preference, outputs should be short and simple, and not require
downstream services to do complicated parsing in order to use them.
Shell commands in Liminix are run using the Busybox shell which
doesn't have the niceties of an advanced shell like Bash let alone
those of a real programming language.
Note also that the Lua :code:`svc` library only reads the first line
of each output.
..
TODO: outputs that change, and services that poll other services
Module implementation
*********************
@ -192,7 +415,7 @@ To expose a service template in a module, it needs the following:
.. code-block:: nix
config.system.service.cowsay = liminix.callService ./service.nix {
config.system.service.cowsay = config.system.callService ./service.nix {
address = mkOption {
type = types.str;
default = "0.0.0.0";

View File

@ -88,64 +88,6 @@ time with configurations for RP-PPPoE and/or Accel PPP.`
Hardware devices
****************
.. _serial:
U-Boot and serial shenanigans
=============================
Every device that we have so far encountered in Liminix uses `U-Boot,
the "Universal Boot Loader" <https://docs.u-boot.org/en/latest/>`_ so
it's worth knowing a bit about it. "Universal" is in this context a
bit of a misnomer, though: encountering *mainline* U-Boot is very rare
and often you'll find it is a fork from some version last updated
in 2008. Upgrading U-Boot is more or less complicated depending on the
device and is outside scope for Liminix.
To speak to U-Boot on your device you'll usually need a serial
connection to it. This is device-specific. Usually it involves
opening the box, locating the serial header pins (TX, RX and GND) and
connecting a USB TTL converter to them.
The Rolls Royce of USB/UART cables is the `FTDI cable
<https://cpc.farnell.com/ftdi/ttl-232r-rpi/cable-debug-ttl-232-usb-rpi/dp/SC12825?st=usb%20to%20uart%20cable>`_,
but there are cheaper alternatives based on the PL2303 and CP2102 chipsets. Or
get creative and use the `UART GPIO pins <https://pinout.xyz/>`_ on a Raspberry Pi. Whatever you do, make sure
that the voltages are compatible: if your device is 3.3V (this is
typical but not universal), you don't want to be sending it 5v or
(even worse) 12v.
Run a terminal emulator such as Minicom on the computer at other end
of the link. 115200 8N1 is the typical speed.
.. NOTE::
TTL serial connections typically have no form of flow control and
so don't always like having massive chunks of text pasted into
them - and U-Boot may drop characters while it's busy. So don't
necessarily expect to copy-paste large chunks of text into the
terminal emulator and have it work just like that.
If using Minicom, you may find it helps to bring up the "Termimal
settings" dialog (C^A T), then configure "Newline tx delay" to
some small but non-zero value.
When you turn the router on you should be greeted with some messages
from U-Boot, followed by the instruction to hit some key to stop
autoboot. Do this and you will get to the prompt. If you didn't see
anything, the strong likelihood is that TX and RX are the wrong way
around. If you see garbage, try a different speed.
Interesting commands to try first in U-Boot are :command:`help` and
:command:`printenv`.
To do anything useful with U-Boot you will probably need a way to get
large binary files onto the device, and the usual way to do this is by
adding a network connection and using TFTP to download them. It's
quite common that the device's U-Boot doesn't speak DHCP so it will
need a static LAN address. You might also want to keep it away from
your "real" LAN: see :ref:`bng` for some potentially useful tooling
to use it on an isolated network.
TFTP
====
@ -290,18 +232,13 @@ Running tests
*************
You can run all of the tests by evaluating :file:`ci.nix`, which is the
input I use in Hydra. Note that it expects Nixpkgs stable `and` unstable
as inputs, because it builds the qemu device against both.
input I use in Hydra.
.. code-block:: console
nix-build --argstr liminix `pwd` --arg nixpkgs "<nixpkgs>" \
--argstr unstable `pwd`/../unstable-nixpkgs/ ci.nix
To run a single named test, use the ``-A`` flag. For example, ``-A pppoe``
nix-build -I liminix=`pwd` ci.nix -A pppoe # run one job
nix-build -I liminix=`pwd` ci.nix -A all # run all jobs
Troubleshooting
***************

View File

@ -1,11 +1,9 @@
{ 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;
@ -26,7 +24,6 @@ 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)

View File

@ -1,29 +1,36 @@
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;
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;
devices = filter (n: n != "families") (lib.mapAttrsToList (n: t: n) (readDir ../devices));
texts = map (
n:
let
d = import ../devices/${n}/default.nix;
tag = ".. _${lib.strings.replaceStrings [" "] ["-"] n}:";
d' = {
description = ''
${n}
${substring 0 (stringLength n) "********************************"}
'';
} // d;
in
"${tag}\n\n${d'.description}"
) devices;
in
writeText "hwdoc" ''
Supported hardware
##################
For development, the `GL.iNet GL-MT300A <https://www.gl-inet.com/products/gl-mt300a/>`_
is an attractive choice as it has a builtin "debrick" procedure in the
boot monitor and is also comparatively simple to
attach serial cables to (soldering not required), so it
is lower-risk than some devices.
For a more powerful device, something with an ath10k would be the safe bet,
or the Linksys E8450 which seems popular in the openwrt community.
${lib.concatStringsSep "\n\n" texts}
''

View File

@ -7,6 +7,7 @@ Liminix
intro
tutorial
installation
configuration
admin
development

211
doc/installation.rst Normal file
View File

@ -0,0 +1,211 @@
Installation
############
Hardware devices vary wildly in their affordances for installing new
operating systems, so it should be no surprise that the Liminix
installation procedure is hardware-dependent. This section contains
generic instructions, but please refer to the documentation for your
device to find whether and how well they apply.
Building a firmware image
*************************
Liminix uses the Nix language to provide congruent configuration
management. This means that to change anything about the way in
which a Liminix system works, you make that change in
your :file:`configuration.nix` (or one of the other files it references),
and rerun :command:`nix-build` to action
the change. It is not possible (at least, without shenanigans) to make
changes by logging into the device and running imperative commands
whose effects may later be overridden: :file:`configuration.nix`
always describes the entire system and can be used to recreate that
system at any time. You can usefully keep it under version control.
If you are familiar with NixOS, you will notice some similarities
between NixOS and Liminix configuration, and also some
differences. Sometimes the differences are due to the
resource-constrained devices we deploy onto, sometimes due to
differences in the uses these devices are put to.
For a more full description of how to configure Liminix, see
:ref:`configuration`. Assuming for the moment that you want a typical
home wireless gateway/router, the best way to get started is to copy
:file:`examples/rotuer.nix` and edit it for your requirements.
.. code-block:: console
$ cp examples/rotuer.nix configuration.nix
$ vi configuration.nix # other editors are available
$ # adjust this next command for your hardware device
$ nix-build -I liminix-config=./configuration.nix \
--arg device "import ./devices/gl-mt300a" -A outputs.default
Usually (not always, *please check the documentation for your device*)
this will leave you with a file :file:`result/firmware.bin`
which you now need to flash to the device.
Flashing from the boot monitor (TFTP install)
*********************************************
If you are prepared to open the device and have a TTL serial adaptor
of some kind to connect it to, you can probably use U-Boot and a TFTP
server to download and flash the image.
This is quite hardware-specific and may even involve soldering - see
the documention for your device. However, it is in some ways the most
"reliable" option: if you can see what's happening (or not happening)
in early boot, the risk of "bricking" is substantially reduced and you
have options for recovering if you misstep or flash a bad image.
.. _serial:
U-Boot and serial shenanigans
=============================
Every device that we have so far encountered in Liminix uses `U-Boot,
the "Universal Boot Loader" <https://docs.u-boot.org/en/latest/>`_ so
it's worth knowing a bit about it. "Universal" is in this context a
bit of a misnomer, though: encountering *mainline* U-Boot is very rare
and often you'll find it is a fork from some version last updated
in 2008. Upgrading U-Boot is more or less complicated depending on the
device and is outside scope for Liminix.
To speak to U-Boot on your device you'll usually need a serial
connection to it. This typically involves opening the box, locating
the serial header pins (TX, RX and GND) and connecting a USB TTL
converter to them.
The Rolls Royce of USB/UART cables is the `FTDI cable
<https://cpc.farnell.com/ftdi/ttl-232r-rpi/cable-debug-ttl-232-usb-rpi/dp/SC12825?st=usb%20to%20uart%20cable>`_,
but there are cheaper alternatives based on the PL2303 and CP2102 chipsets - or you could even
get creative and use the `UART GPIO pins <https://pinout.xyz/>`_ on a Raspberry Pi. Whatever you do, make sure
that the voltages are compatible: if your device is 3.3V (this is
typical but not universal), you don't want to be sending it 5v or
(even worse) 12v.
Run a terminal emulator such as Minicom on the computer at other end
of the link. 115200 8N1 is the typical speed.
.. NOTE::
TTL serial connections often have no flow control and
so don't always like having massive chunks of text pasted into
them - and U-Boot may drop characters while it's busy. So don't
do that.
If using Minicom, you may find it helps to bring up the "Termimal
settings" dialog (C^A T), then configure "Newline tx delay" to
some small but non-zero value.
When you turn the router on you should be greeted with some messages
from U-Boot, followed by the instruction to hit some key to stop
autoboot. Do this and you will get to the prompt. If you didn't see
anything, the strong likelihood is that TX and RX are the wrong way
around. If you see garbage, try a different speed.
Interesting commands to try first in U-Boot are :command:`help` and
:command:`printenv`.
You will also need to configure a TFTP server on a network that's
accessible to the device: how you do that will vary according to which
TFTP server you're using and so is out of scope for this document.
Buildiing and installing the image
==================================
Follow the device-specific instructions for "TFTP install": usually,
the steps are
* build the `outputs.mtdimage` output
* copy :file:`result/firmware.bin` to your TFTP server
* copy/paste the commands in :file:`result/flash.scr` one at a time into the U-Boot command line
* reset the device
You should now see messages from U-Boot, then from the Linux kernel
and eventually a shell prompt.
.. NOTE:: Before you reboot, check which networks the device is
plugged into, and disconnect as necessary. If you've just
installed a DHCP server or anything similar that responds to
broadcasts, you may not want it to do that on the network
that you temporarily connected it to for installing it.
Flashing from OpenWrt
*********************
.. CAUTION:: Untested! A previous version of these instructions
(without the -e flag) led to bricking the device
when flashing a jffs2 image. If you are reading
this message, nobody has yet reported on whether the
new instructions are any better.
If your device is running OpenWrt then it probably has the
:command:`mtd` command installed. Build the `outputs.mtdimage` output
(as you would for a TFTP install) and then transfer
:file:`result/firmware.bin` onto the device using e.g.
:command:`scp`. Now flash as follows:
.. code-block:: console
mtd -e -r write /tmp/firmware.bin firmware
The options to this command are for "erase before writing" and "reboot
after writing".
For more information, please see the `OpenWrt manual <https://openwrt.org/docs/guide-user/installation/sysupgrade.cli>`_ which may also contain (hardware-dependent) instructions on how to flash an image using the vendor firmware - perhaps even from a web interface.
Flashing from Liminix
*********************
If the device is already running Liminix and has been configured with
:command:`levitate`, you can use that to safely flash your new image.
Refer to :ref:`levitate` for an explanation.
If the device is running Liminix but doesn't have :command:`levitate`
your options are more limited. You may attempt to use
:command:`flashcp` but it doesn't always work: as it copies the new
image over the top of the active root filesystem, surprise may ensue.
Consider instead using a serial connection: you may need one anyway
after trying flashcp if it corrupts the image.
flashcp (not generally recommended)
===================================
Connect to the device and locate the "firmware" partition, which you
can do with a combination of :command:`dmesg` output and the contents
of :file:`/proc/mtd`
.. code-block:: console
<5>[ 0.469841] Creating 4 MTD partitions on "spi0.0":
<5>[ 0.474837] 0x000000000000-0x000000040000 : "u-boot"
<5>[ 0.480796] 0x000000040000-0x000000050000 : "u-boot-env"
<5>[ 0.487056] 0x000000050000-0x000000060000 : "art"
<5>[ 0.492753] 0x000000060000-0x000001000000 : "firmware"
# cat /proc/mtd
dev: size erasesize name
mtd0: 00040000 00001000 "u-boot"
mtd1: 00010000 00001000 "u-boot-env"
mtd2: 00010000 00001000 "art"
mtd3: 00fa0000 00001000 "firmware"
mtd4: 002a0000 00001000 "kernel"
mtd5: 00d00000 00001000 "rootfs"
Copy :file:`result/firmware.bin` to the device and now run (in this
example)
.. code-block:: console
flashcp -v firmware.bin /dev/mtd3

View File

@ -1,4 +1,4 @@
Module options
##############
.. include:: modules-generated.rst
.. include:: modules-generated.inc.rst

View File

@ -10,4 +10,4 @@ different artefacts, or have different ways to get that artefact
installed. The options available for a particular device are described in
the section for that device.
.. include:: outputs-generated.rst
.. include:: outputs-generated.inc.rst

View File

@ -16,4 +16,4 @@
(each [_ option (ipairs (sorted-options (yaml.load (io.read "*a"))))]
(when (and (output? option) (not option.internal))
(print (.. ".. _" (string.gsub option.name "%." "-") ":") "\n")
(print option.description)))
(print option.description "\n")))

View File

@ -71,7 +71,7 @@ stdin/stdout.
You should now see Linux boot messages and after a few seconds be
presented with a root shell prompt. You can run commands to look at
the filesystem, see what processes are running, view log messages (in
:file:/run/uncaught-logs.current), etc. To kill the emulator, press ^P
:file:/run/log/current), etc. To kill the emulator, press ^P
(Control P) then c to enter the "QEMU Monitor", then type ``quit`` at
the ``(qemu)`` prompt.
@ -138,6 +138,8 @@ unbrick if necessary.
work here, but you accept the slightly greater bricking
risk if it doesn't.
See :doc:`hardware` for device support status.
You may want to read and inwardly digest the Develoment Manual section
:ref:`serial` when you start working with Liminix on real hardware. You
won't *need* serial access for this example, assuming it works, but it
@ -291,14 +293,17 @@ the hostname) and then run
.. code-block:: console
nix-shell --run "liminix-rebuild root@address-of-the-device -I liminix-config=./my-router.nix --arg device "import ./devices/gl-ar750""
nix-build -I liminix-config=./my-router.nix \
--arg device "import ./devices/gl-ar750" \
-A outputs.systemConfiguration && \
result/install.sh root@address-of-the-device
(This requires the device to be network-accessible from your build
machine, which for a test/demo system might involve a second network
device in your build system - USB ethernet adapters are cheap - or
a bit of messing around unplugging cables.)
For more information about :code:`liminix-rebuild`, see the manual section :ref:`admin:Rebuilding the system`.
For more information about in-place-updates, see the manual section :ref:`Rebuilding the system`.
Final thoughts

View File

@ -11,15 +11,15 @@
...
}: let
secrets = import ./extneder-secrets.nix;
inherit (pkgs.liminix.services) oneshot longrun bundle target;
inherit (pkgs.liminix.services) oneshot longrun target;
inherit (pkgs.pseudofile) dir symlink;
inherit (pkgs) writeText dropbear ifwait serviceFns;
inherit (pkgs) writeText serviceFns;
svc = config.system.service;
in rec {
boot = {
tftp = {
serverip = "192.168.8.148";
ipaddr = "192.168.8.251";
serverip = "10.0.0.1";
ipaddr = "10.0.0.8";
};
};
@ -28,34 +28,12 @@ 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;
@ -74,7 +52,6 @@ in rec {
dependencies = [ services.dhcpc ];
name = "resolvconf";
up = ''
. ${serviceFns}
( in_outputs ${name}
for i in $(output ${services.dhcpc} dns); do
echo "nameserver $i" > resolv.conf
@ -105,7 +82,7 @@ in rec {
};
services.mount_external_disk = svc.mount.build {
device = "LABEL=backup-disk";
partlabel = "backup-disk";
mountpoint = "/srv";
fstype = "ext4";
};
@ -115,7 +92,6 @@ in rec {
secrets_file = oneshot rec {
name = "rsync-secrets";
up = ''
. ${serviceFns}
(in_outputs ${name}
echo "backup:${secrets.rsync_secret}" > secrets)
'';
@ -141,23 +117,37 @@ in rec {
secrets_file
services.mount_external_disk
config.hardware.networkInterfaces.lan
] ;
];
};
users.root = {
passwd = lib.mkForce secrets.root_password;
# openssh.authorizedKeys.keys = [
# (builtins.readFile "/home/dan/.ssh/id_rsa.pub")
# ];
passwd = lib.mkForce secrets.root.passwd;
openssh.authorizedKeys.keys = secrets.root.keys;
};
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 strace tcpdump ];
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;
};
})
];
}

View File

@ -5,9 +5,9 @@
# wherever the text "EDIT" appears - please consult the tutorial
# documentation for details.
{ config, pkgs, lib, ... } :
{ config, pkgs, ... }:
let
inherit (pkgs.liminix.services) bundle oneshot longrun;
inherit (pkgs.liminix.services) bundle oneshot;
inherit (pkgs) serviceFns;
# EDIT: you can pick your preferred RFC1918 address space
# for NATted connections, if you don't like this one.
@ -49,31 +49,40 @@ 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 { };
@ -128,7 +137,6 @@ in 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
@ -157,9 +165,7 @@ in rec {
interface = services.wan;
};
services.firewall = svc.firewall.build {
ruleset = import ./demo-firewall.nix;
};
services.firewall = svc.firewall.build { };
services.packet_forwarding = svc.network.forward.build { };
@ -196,7 +202,5 @@ in rec {
];
};
defaultProfile.packages = with pkgs; [
min-collect-garbage
];
defaultProfile.packages = with pkgs; [ min-collect-garbage ];
}

View File

@ -8,12 +8,10 @@
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 = {
@ -24,113 +22,32 @@ in rec {
};
imports = [
../modules/wlan.nix
../modules/vlan
../modules/network
../modules/hostapd
../modules/bridge
../modules/ssh
"${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";
};
};
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; [
profile.wap = {
interfaces = 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";
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;
};
};
};
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.dhcpc} router)";
target = "default";
dependencies = [services.dhcpc];
};
users.root.passwd = lib.mkForce secrets.root_password;
services.sshd = svc.ssh.build {};
users.root.passwd = lib.mkForce secrets.root.passwd;
defaultProfile.packages = with pkgs; [nftables strace tcpdump swconfig];
}

View File

@ -1,6 +1,5 @@
{ config, pkgs, lib, ... } :
{ config, pkgs, ... } :
let
inherit (pkgs) serviceFns;
svc = config.system.service;
in rec {

View File

@ -1,6 +1,5 @@
{ config, pkgs, lib, ... } :
{ config, pkgs, ... } :
let
inherit (pkgs) serviceFns;
svc = config.system.service;
in rec {

View File

@ -0,0 +1,81 @@
# This is "contrib"-level code. This module solves a particular
# problem for my particular setup and is provided as is, as an example
# of how you might write something similar if you had a similar
# problem. Don't expect it to work unmolested in your setup (you will
# at the absolute minimum have to change the domain name), nor even to
# continue to exist without possibly being changed beyond recognition.
# The computers on my LAN have globally unique routable IPv6
# addresses, but I have only one public IPv4 address. I want to expose
# HTTPS services to the Internet _whatever_ machine is hosting them,
# so I publish an AAAA record to the machine itself, and an A record
# to the public v4 address of the router which is running this nginx.
# This nginx checks the SNI in the incoming connection and forwards
# the connection to the (IPv6 address of the) same hostname
# See https://ww.telent.net/2020/12/2/six_into_4_won_t_go for
# the original solution to this problem, which used sniproxy (now
# unmaintained) instead of nginx
{ config, pkgs, ... }:
let
inherit (pkgs.liminix.services) longrun;
inherit (pkgs) writeText;
in {
config = {
users.nginx = {
uid = 52; gid= 52;
dir = "/run/";
shell = "/bin/false";
};
groups.nginx = {
gid= 52;
usernames = ["nginx"];
};
services.sniproxy =
let
nginx = pkgs.nginx-small.override {
pcre = null;
zlib = null;
options = [
"stream"
"stream_ssl_module" "stream_ssl_preread_module"
"stream_map_module"
];
};
conf = writeText "nginx.conf" ''
worker_processes auto;
error_log /proc/self/fd/1 info;
pid /dev/null;
user nginx;
daemon off;
events {
worker_connections 1024;
}
stream {
log_format proxy '$remote_addr -> $ssl_target';
access_log /proc/self/fd/1 proxy;
map $ssl_preread_server_name $ssl_target {
hostnames;
.telent.net $ssl_preread_server_name:443;
}
server {
listen 443;
resolver 127.0.0.1 ipv6=on ipv4=off;
resolver_timeout 1s;
proxy_pass $ssl_target;
ssl_preread on;
}
}
'';
in longrun {
name = "sniproxy";
run = ''
${nginx}/bin/nginx -c ${conf}
'';
};
};
}

119
examples/nwa50ax-ap.nix Normal file
View File

@ -0,0 +1,119 @@
{ 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 ];
}

113
examples/recovery.nix Normal file
View File

@ -0,0 +1,113 @@
{ config, pkgs, lib, ... } :
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" { } ''
mkdir -p $out/bin
cd ${pkgs.util-linux-small}/bin
cp fdisk sfdisk mkswap $out/bin
'';
in rec {
imports = [
../modules/network
../modules/ssh
../modules/usb.nix
../modules/schnapps
../modules/outputs/mtdimage.nix
../modules/outputs/mbrimage.nix
../modules/outputs/tftpboot.nix
../modules/outputs/ubifs.nix
../modules/outputs/ubimage.nix
../modules/outputs/jffs2.nix
../modules/outputs/ext4fs.nix
];
kernel.config = {
BTRFS_FS = "y";
};
boot.tftp = {
ipaddr = "10.0.0.8"; # my address
serverip = "10.0.0.1"; # build machine or other tftp server
freeSpaceBytes = 1024 * 1024 * 4;
};
boot.loader.extlinux.enable = true;
hostname = "liminix-recovery";
services.dhcpc = svc.network.dhcp.client.build {
interface = config.hardware.networkInterfaces.lan2;
# don't start DHCP until the hostname is configured,
# so it can identify itself to the DHCP server
dependencies = [ config.services.hostname ];
};
services.sshd = svc.ssh.build {
dependencies = [ config.services.growfs ];
};
services.defaultroute4 = svc.network.route.build {
via = "$(output ${services.dhcpc} router)";
target = "default";
dependencies = [ services.dhcpc ];
};
services.resolvconf = oneshot rec {
dependencies = [ services.dhcpc ];
name = "resolvconf";
up = ''
( in_outputs ${name}
for i in $(output ${services.dhcpc} dns); do
echo "nameserver $i" > resolv.conf
done
)
'';
};
services.growfs = let name = "growfs"; in oneshot {
inherit name;
up = ''
device=$(grep /persist /proc/1/mountinfo | cut -f9 -d' ')
${pkgs.e2fsprogs}/bin/resize2fs $device
'';
};
filesystem = dir {
etc = dir {
"resolv.conf" = symlink "${services.resolvconf}/.outputs/resolv.conf";
};
mnt = dir {};
};
rootfsType = "ext4";
# sda is most likely correct for the boot-from-USB case. For tftp
# it's overridden by the boot.scr anyway, so maybe it all works out
hardware.rootDevice = lib.mkForce "/dev/sda1";
users.root = {
# the password is "secret". Use mkpasswd -m sha512crypt to
# create this hashed password string
passwd = "$6$y7WZ5hM6l5nriLmo$5AJlmzQZ6WA.7uBC7S8L4o19ESR28Dg25v64/vDvvCN01Ms9QoHeGByj8lGlJ4/b.dbwR9Hq2KXurSnLigt1W1";
openssh.authorizedKeys.keys =
let fromBuild =
(builtins.readFile
((builtins.toPath (builtins.getEnv "HOME")) + "/.ssh/authorized_keys")
);
in lib.splitString "\n" fromBuild;
};
defaultProfile.packages = with pkgs; [
e2fsprogs # ext4
btrfs-progs
mtdutils # mtd, jffs2, ubifs
dtc # you never know when you might need device tree stuff
some-util-linux
libubootenv # fw_{set,print}env
pciutils
];
}

View File

@ -1,5 +1,6 @@
rec {
{
wpa_passphrase = "you bring light in";
ssid = "liminix";
l2tp = {
name = "abcde@a.1";
password = "NotMyIspPassword";
@ -7,8 +8,10 @@ rec {
root = {
# mkpasswd -m sha512crypt
passwd = "$6$6pt0mpbgcB7kC2RJ$kSBoCYGyi1.qxt7dqmexLj1l8E6oTZJZmfGyJSsMYMW.jlsETxdgQSdv6ptOYDM7DHAwf6vLG0pz3UD31XBfC1";
openssh.authorizedKeys.keys = [
];
openssh.authorizedKeys.keys = [ ];
};
lan = {
prefix = "10.8.0";
};
root_password = root.passwd;
}

View File

@ -1,25 +1,19 @@
# 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.
# 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
{ config, pkgs, lib, ... } :
{ config, pkgs, lib, modulesPath, ... } :
let
secrets = import ./rotuer-secrets.nix;
inherit (pkgs.liminix.services) oneshot longrun bundle;
inherit (pkgs) serviceFns;
secrets = {
domainName = "fake.liminix.org";
firewallRules = { };
} // (import ./rotuer-secrets.nix);
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;
};
@ -33,53 +27,71 @@ in rec {
};
imports = [
../modules/wlan.nix
../modules/network
../modules/ppp
../modules/dnsmasq
../modules/dhcp6c
../modules/firewall
../modules/hostapd
../modules/bridge
../modules/ntp
../modules/ssh
"${modulesPath}/profiles/gateway.nix"
];
hostname = "rotuer";
services.hostap = svc.hostapd.build {
interface = config.hardware.networkInterfaces.wlan;
params = {
ssid = "liminix";
hw_mode="g";
channel = "2";
ieee80211n = 1;
} // wirelessConfig;
};
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 (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 = {
# 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 = {
enable = true;
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 :-).
services.hostap5 = svc.hostapd.build {
interface = config.hardware.networkInterfaces.wlan5;
params = rec {
ssid = "liminix_5";
hw_mode="a";
channel = 36;
ht_capab = "[HT40+]";
vht_oper_chwidth = 1;
vht_oper_centr_freq_seg0_idx = channel + 6;
ieee80211ac = 1;
} // wirelessConfig;
};
services.int = svc.network.address.build {
interface = svc.bridge.primary.build { ifname = "int"; };
family = "inet"; address ="10.8.0.1"; prefixLength = 16;
};
services.bridge = svc.bridge.members.build {
primary = services.int;
members = with config.hardware.networkInterfaces;
[ wlan
wlan5
lan ];
"${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 {
@ -91,98 +103,20 @@ in rec {
users.root = secrets.root;
services.dns =
let interface = services.int;
in svc.dnsmasq.build {
resolvconf = services.resolvconf;
inherit interface;
ranges = [
"10.8.0.10,10.8.0.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);
domain = "fake.liminix.org";
};
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 = import ./demo-firewall.nix;
};
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"
];
programs.busybox = {
applets = [
"fdisk" "sfdisk"
];
options = {
FEATURE_FANCY_TAIL = "y";
};
};
}

View File

@ -0,0 +1,211 @@
# A demonstration config for a home/soho router with PPPoE upstream
# and fallback to an L2TP tunnel over a USB WWAN device
{
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.liminix) outputRef;
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 = {
serverip = "10.0.0.1";
ipaddr = "10.0.0.8";
};
};
imports = [
../modules/wwan
../modules/network
../modules/ssh
../modules/usb.nix
../modules/ppp
../modules/round-robin
../modules/health-check
../modules/secrets
../modules/profiles/gateway.nix
];
hostname = "thing";
services.wan-address-for-secrets = svc.network.address.build {
interface = config.hardware.networkInterfaces.wan;
family = "inet"; address ="10.0.0.10"; prefixLength = 24;
};
services.secrets = svc.secrets.outboard.build {
name = "secret-service";
url = "http://10.0.0.1/liminix/examples/real-secrets.json";
username = "demo";
password = "demo";
interval = 5;
dependencies = [ services.wan-address-for-secrets ];
};
services.wwan = svc.wwan.huawei-e3372.build {
apn = "data.uk";
username = "user";
password = "one2one";
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;
};
dhcp = {
start = 10;
end = 240;
hosts = { } // lib.optionalAttrs (builtins.pathExists ./static-leases.nix) (import ./static-leases.nix);
localDomain = "lan";
};
};
wan =
let
secret = outputRef config.services.secrets;
username = secret "ppp/username";
password = secret "ppp/password";
in {
interface =
let
pppoe = svc.pppoe.build {
interface = config.hardware.networkInterfaces.wan;
debug = true;
inherit username password;
};
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];
};
l2tpd= svc.l2tp.build {
lns = lns.address;
inherit username password;
dependencies = [config.services.lns-address route check-address];
};
in
svc.health-check.build {
service = l2tpd;
threshold = 3;
interval = 2;
healthCheck = pkgs.writeAshScript "ping-check" {} "ping 1.1.1.1";
};
in svc.round-robin.build {
name = "wan";
services = [
pppoe
l2tp
];
};
dhcp6.enable = true;
};
wireless.networks = {
"${rsecrets.ssid}" = {
interface = config.hardware.networkInterfaces.wlan;
hw_mode = "g";
channel = "6";
ieee80211n = 1;
} // wirelessConfig // {
wpa_passphrase = outputRef config.services.secrets "wpa_passphrase";
};
"${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 // {
wpa_passphrase = outputRef config.services.secrets "wpa_passphrase";
};
};
};
services.bootstrap-dhcpc = svc.network.dhcp.client.build {
interface = config.services.wwan;
dependencies = [ config.services.hostname ];
};
services.sshd = svc.ssh.build {
authorizedKeys = outputRef config.services.secrets "ssh/authorizedKeys";
};
services.lns-address = let
ns = "$(output_word ${services.bootstrap-dhcpc} dns 1)";
route-to-bootstrap-nameserver = svc.network.route.build {
via = "$(output ${services.bootstrap-dhcpc} router)";
target = ns;
dependencies = [services.bootstrap-dhcpc];
};
in oneshot rec {
name = "resolve-l2tp-server";
dependencies = [ services.bootstrap-dhcpc route-to-bootstrap-nameserver ];
up = ''
(in_outputs ${name}
DNSCACHEIP="${ns}" ${pkgs.s6-dns}/bin/s6-dnsip4 ${lns.hostname} \
> addresses
)
'';
};
users.root = rsecrets.root;
programs.busybox.options = {
FEATURE_FANCY_TAIL = "y";
};
}

19
examples/secrets.json Normal file
View File

@ -0,0 +1,19 @@
{
"wpa_passphrase": "you bring light in",
"ssid": "liminix",
"l2tp": {
"name": "abcde@a.1",
"password": "NotMyIspPassword"
},
"root": {
"passwd": "$6$6pt0mpbgcB7kC2RJ$kSBoCYGyi1.qxt7dqmexLj1l8E6oTZJZmfGyJSsMYMW.jlsETxdgQSdv6ptOYDM7DHAwf6vLG0pz3UD31XBfC1",
"openssh": {
"authorizedKeys": {
"keys": [ ]
}
}
},
"lan": {
"prefix": "10.8.0"
}
}

View File

@ -1,6 +1,5 @@
{ config, pkgs, lib, lim, ... } :
{ config, pkgs, lim, ... } :
let
inherit (pkgs) serviceFns;
svc = config.system.service;
in rec {

View File

@ -9,28 +9,28 @@
./busybox.nix
./dhcp6c
./dnsmasq
./outputs/ext4fs.nix
./firewall
./hardware.nix
./hostapd
./hostname.nix
./outputs/initramfs.nix
./outputs/jffs2.nix
./kernel.nix
./outputs/kexecboot.nix
./kernel
./mdevd.nix
./mount
./network
./ntp
./outputs.nix
./outputs/vmroot.nix
./outputs/ubimage.nix
./outputs/ext4fs.nix
./outputs/initramfs.nix
./outputs/jffs2.nix
./outputs/kexecboot.nix
./outputs/mtdimage.nix
./ppp
./ramdisk.nix
./squashfs.nix
./ssh
./outputs/tftpboot.nix
./outputs/ubifs.nix
./outputs/ubimage.nix
./outputs/vmroot.nix
./ppp
./ramdisk.nix
./ssh
./users.nix
./vlan
./watchdog

View File

@ -1,4 +1,4 @@
{ lib, lim, pkgs, config, ...}:
{ lim, pkgs, config, ...}:
{
config = {
kernel.config = {

View File

@ -1,4 +1,4 @@
{ lib, lim, pkgs, config, ...}:
{ lim, pkgs, config, ...}:
{
config = {
kernel.config = {

View File

@ -1,4 +1,4 @@
{ lib, pkgs, config, lim, ...}:
{ config, lim, ...}:
{
config = {
kernel.config = {

View File

@ -1,4 +1,4 @@
{ lib, pkgs, config, ...}:
{ pkgs, config, ...}:
{
imports = [ ./mips.nix ];
config = {

View File

@ -1,4 +1,4 @@
{ lib, pkgs, config, ...}:
{ config, ...}:
{
imports = [ ./mips.nix ];
config = {

View File

@ -4,17 +4,12 @@
{ lib, pkgs, config, ...}:
let
inherit (lib) mkEnableOption mkOption types isDerivation hasAttr ;
inherit (lib) mkOption types;
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.nix # kernel is a separate module for doc purposes
];
options = {
defaultProfile = {
packages = mkOption {
@ -29,6 +24,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 = ''
@ -37,23 +36,47 @@ in {
'';
# internal = true; # probably a good case to make this internal
};
rootfsType = mkOption {
rootfsType = mkOption {
default = "squashfs";
type = types.enum [
"btrfs"
"ext4"
"jffs2"
"squashfs"
"ubifs"
];
};
rootOptions = mkOption {
type = types.nullOr types.str;
default = null;
};
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 = {
@ -69,7 +92,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
@ -94,7 +117,31 @@ in {
"root=${config.hardware.rootDevice}"
"rootfstype=${config.rootfsType}"
"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/";
@ -134,6 +181,7 @@ in {
proc = dir {};
run = dir {};
sys = dir {};
tmp = dir {};
};
};
}

View File

@ -10,10 +10,11 @@
{ 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; };
@ -21,13 +22,13 @@ in
};
};
config.system.service.bridge = {
primary = liminix.callService ./primary.nix {
primary = config.system.callService ./primary.nix {
ifname = mkOption {
type = types.str;
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 +48,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";
};
};
}

View File

@ -1,23 +1,28 @@
{
liminix
, ifwait
, lib
, svc
}:
{ members, primary } :
let
inherit (liminix.networking) interface;
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";

View File

@ -1,12 +1,10 @@
{
liminix
, ifwait
, lib
}:
{ ifname } :
let
inherit (liminix.services) bundle oneshot;
inherit (lib) mkOption types;
inherit (liminix.services) oneshot;
in oneshot rec {
name = "${ifname}.link";
up = ''

View File

@ -8,7 +8,7 @@
{ lib, pkgs, config, ...}:
let
inherit (lib) mkOption mkEnableOption types mapAttrsToList;
inherit (lib) mkOption types mapAttrsToList;
inherit (pkgs.pseudofile) dir symlink;
inherit (lib.strings) toUpper;
@ -32,23 +32,21 @@ 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" "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" "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"
];
in {
options = {
@ -87,10 +85,13 @@ 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
);
};
};
}

View File

@ -1,31 +1,32 @@
(local { : system } (require :anoia))
(local svc (require :anoia.svc))
(fn changes [old-addresses new-addresses]
(let [added {}
deleted {}]
(each [n address (pairs new-addresses)]
(if (not (. old-addresses n))
(table.insert added address)))
(fn deletions [old-addresses new-addresses]
(let [deleted {}]
(each [n address (pairs old-addresses)]
(if (not (. new-addresses n))
(table.insert deleted address)))
(values added deleted)))
(let [now (. new-addresses n)]
(if (or (not now) (not (= now.len address.len)))
(table.insert deleted address))))
deleted))
(fn update-prefixes [device prefixes new-prefixes]
(let [(added deleted) (changes prefixes new-prefixes)]
(each [_ p (ipairs added)]
(system
(.. "ip address add " p.address "1/" p.len " dev " device)))
(each [_ p (ipairs deleted)]
(system
(.. "ip address del " p.address "1/" p.len " dev " device)))))
(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 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")))))
(update-prefixes lan-device addresses (or (v:output "prefix") []) system))))
{ : changes : run }

View File

@ -2,9 +2,9 @@
writeFennel
, linotify
, anoia
, lua
, lualinux
}:
writeFennel "acquire-delegated-prefix" {
packages = [ linotify anoia lua.pkgs.luafilesystem ];
packages = [ linotify anoia lualinux ];
mainFunction = "run";
} ./acquire-delegated-prefix.fnl

View File

@ -1,68 +1,124 @@
(local subject (require :acquire-wan-address))
(local { : view } (require :fennel))
(import-macros { : expect= } :anoia.assert)
(local { : merge : dup } (require :anoia))
;; nix-shell --run "cd modules/dhcp6c && fennelrepl acquire-wan-address-test.fnl"
(local a1
{
"2001-ab-cd-ef_hjgKHGhKJH" {
:address "2001:ab:cd:ef"
:len "64"
:preferred "200"
:valid "200"
}
"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"
}
}
)
(local a2
{
"2001-0-1-2-3_aNteBnb" {
:address "2001:0:1:2:3"
:len "64"
:preferred "200"
:valid "200"
}
"2001-0-1-2-3" {
:address "2001:0:1:2:3"
:len "64"
:preferred "3600"
:valid "7200"
}
}
)
(macro expect [assertion]
(let [msg (.. "expectation failed: " (view assertion))]
`(when (not ,assertion)
(assert false ,msg))))
(local a21
{
"2001-0-1-2-3" {
:address "2001:0:1:2:3"
:len "64"
:preferred "1800"
:valid "5400"
}
}
)
(fn first-address []
(let [(add del)
(subject.changes
(let [deleted
(subject.deletions
{ }
a1
)]
(expect (= (# del) 0))
(expect (= (# add) 1))
(let [[first] add]
(expect (= first.address "2001:ab:cd:ef")))))
(expect= deleted [])))
(fn second-address []
(let [(add del)
(subject.changes
(let [del
(subject.deletions
a1
(merge (dup a1) a2)
)]
(expect (= (# del) 0))
(expect (= (# add) 1))
(let [[first] add] (expect (= first.address "2001:0:1:2:3")))))
(expect= del [])))
(fn less-address []1
(let [(add del)
(subject.changes
(fn old-address-is-deleted []
(let [del
(subject.deletions
(merge (dup a1) a2)
a1
)]
(expect (= (# add) 0))
(expect (= (# del) 1))
(expect= (. del 1) (. a2 "2001-0-1-2-3"))
))
(let [[first] del] (expect (= first.address "2001:0:1:2:3")))))
(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 [])))
(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)
(less-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")

View File

@ -1,35 +1,32 @@
(local { : system } (require :anoia))
(local svc (require :anoia.svc))
;; 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 changes [old-addresses new-addresses]
(let [added {}
deleted {}]
(each [n address (pairs new-addresses)]
(if (not (. old-addresses n))
(table.insert added address)))
(fn deletions [old-addresses new-addresses]
(let [deleted {}]
(each [n address (pairs old-addresses)]
(if (not (. new-addresses n))
(table.insert deleted address)))
(values added deleted)))
(let [now (. new-addresses n)]
(if (or (not now) (not (= now.len address.len)))
(table.insert deleted address))))
deleted))
(fn update-addresses [wan-device addresses new-addresses]
(let [(added deleted) (changes addresses new-addresses)]
(each [_ p (ipairs added)]
(system
(.. "ip address add " p.address "/" p.len " dev " wan-device)))
(each [_ p (ipairs deleted)]
(system
(.. "ip address del " p.address "/" p.len " dev " wan-device)))
new-addresses))
(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 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")))))
(update-addresses wan-device addresses (or (v:output "address") []) system))))
{ : update-addresses : changes : run }
{ : update-addresses : deletions : run }

View File

@ -2,9 +2,9 @@
writeFennel
, linotify
, anoia
, lua
, lualinux
}:
writeFennel "acquire-wan-address" {
packages = [ linotify anoia lua.pkgs.luafilesystem ];
packages = [ linotify anoia lualinux ];
mainFunction = "run";
} ./acquire-wan-address.fnl

View File

@ -1,16 +1,14 @@
{
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 {
inherit name;
run = "${script} /run/service-state/${client.name} $(output ${interface} ifname)";
run = "${script} $SERVICE_OUTPUTS/${client.name} $(output ${interface} ifname)";
dependencies = [ client interface ];
}

View File

@ -1,19 +1,17 @@
{
liminix
, lib
, odhcp6c
, odhcp-script
}:
{ interface } :
let
inherit (liminix.services) longrun;
inherit (lib) mkOption types;
name = "dhcp6c.${interface.name}";
in longrun {
inherit name;
notification-fd = 10;
run = ''
export SERVICE_STATE=/run/service-state/${name}
export SERVICE_STATE=$SERVICE_OUTPUTS/${name}
${odhcp6c}/bin/odhcp6c -s ${odhcp-script} -e -v -p /run/${name}.pid -P0 $(output ${interface} ifname)
)
'';

View File

@ -12,7 +12,6 @@
{ lib, pkgs, config, ...}:
let
inherit (lib) mkOption types;
inherit (pkgs.liminix.services) oneshot;
inherit (pkgs) liminix;
in
{
@ -24,13 +23,13 @@ in
};
};
config.system.service.dhcp6c = {
client = liminix.callService ./client.nix {
client = config.system.callService ./client.nix {
interface = mkOption {
type = liminix.lib.types.interface;
description = "interface (usually WAN) to query for DHCP6";
};
};
address = liminix.callService ./address.nix {
address = config.system.callService ./address.nix {
client = mkOption {
type = types.anything; # liminix.lib.types.service;
};
@ -39,7 +38,7 @@ in
description = "interface to assign the address to";
};
};
prefix = liminix.callService ./prefix.nix {
prefix = config.system.callService ./prefix.nix {
client = mkOption {
type = types.anything; # liminix.lib.types.service;
};

View File

@ -1,16 +1,14 @@
{
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 {
inherit name;
run = "${script} /run/service-state/${client.name} $(output ${interface} ifname)";
run = "${script} $SERVICE_OUTPUTS/${client.name} $(output ${interface} ifname)";
dependencies = [ client interface ];
}

View File

@ -16,7 +16,7 @@ in {
};
};
config = {
system.service.dnsmasq = liminix.callService ./service.nix {
system.service.dnsmasq = config.system.callService ./service.nix {
user = mkOption {
type = types.str;
default = "dnsmasq";

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 } @ attrs:
hostOpt = name : { mac, v4, v6, leasetime }:
let v6s = concatStrings (map (a : ",[${a}]") v6);
in "--dhcp-host=${mac},${v4}${v6s},${name},${builtins.toString leasetime}";
in
@ -26,7 +26,6 @@ longrun {
inherit name;
dependencies = [ interface ];
run = ''
. ${serviceFns}
${dnsmasq}/bin/dnsmasq \
--user=${user} \
--domain=${domain} \
@ -41,10 +40,11 @@ longrun {
--no-hosts \
--log-dhcp \
--enable-ra \
--log-debug \
--log-queries \
--log-facility=- \
--dhcp-leasefile=/run/${name}.leases \
--dhcp-leasefile=$(mkstate ${name})/leases \
--pid-file=/run/${name}.pid
'';
# --log-debug \
# --log-queries \
}

View File

@ -35,6 +35,7 @@ in {
(drop "icmpv6 type destination-unreachable ct state invalid,untracked")
];
};
forward-ip6 = {
type = "filter";
family = "ip6";
@ -89,25 +90,29 @@ in {
# accept inbound from the WAN
(if allow-incoming
then accept "oifname \"int\" iifname \"ppp0\""
else "oifname \"int\" iifname \"ppp0\" jump incoming-allowed-ip6"
else "iifname \"ppp0\" jump incoming-allowed-ip6"
)
# allow all outbound and any inbound that's part of a
# recognised (outbound-initiated) flow
(accept "oifname \"int\" iifname \"ppp0\" ct state established,related")
(accept "iifname \"int\" oifname \"ppp0\" ")
"log prefix \"DENIED CHAIN=forward-ip6 \""
];
};
input-lan = {
input-ip6-lan = {
type = "filter";
family = "ip6";
rules = [
(accept "udp dport 547") # dhcp, could restrict to daddr ff02::1:2
(accept "udp dport 53") # dns
(accept "tcp dport 22")
];
};
input-wan = {
input-ip6-wan = {
type = "filter";
family = "ip6";
@ -123,15 +128,16 @@ in {
hook = "input";
rules = [
(accept "meta l4proto icmpv6")
"iifname int jump input-lan"
"iifname ppp0 jump input-wan"
"iifname int jump input-ip6-lan"
"iifname ppp0 jump input-ip6-wan"
(if allow-incoming
then accept "oifname \"int\" iifname \"ppp0\""
else "oifname \"int\" iifname \"ppp0\" jump incoming-allowed-ip6"
then accept "iifname \"ppp0\""
else "iifname \"ppp0\" jump incoming-allowed-ip6"
)
# how does this even make sense in an input chain?
(accept "oifname \"int\" iifname \"ppp0\" ct state established,related")
(accept "iifname \"int\" oifname \"ppp0\" ")
(accept "iifname \"ppp0\" ct state established,related")
(accept "iifname \"int\" ")
"log prefix \"DENIED CHAIN=input-ip6 \""
];
};
@ -154,6 +160,7 @@ in {
"oifname \"ppp0\" masquerade"
];
};
nat-rx = {
type = "nat";
hook = "prerouting";
@ -167,4 +174,71 @@ in {
# packet replies. "
];
};
# these chains are for rules that have to be present for things to
# basically work at all: for example, the router won't issue DHCP
# unless it's allowed to receive DHCP requests. For "site policy"
# rules you may prefer to use incoming-allowed-ip[46] instead
input-ip4-lan = {
type = "filter";
family = "ip";
rules = [
(accept "udp dport 67") # dhcp
(accept "udp dport 53") # dns
(accept "tcp dport 22") # ssh
];
};
input-ip4-wan = {
type = "filter";
family = "ip";
rules = [
(accept "udp sport 53")
];
};
input-ip4 = {
type = "filter";
family = "ip";
policy = "drop";
hook = "input";
rules = [
"iifname lo accept"
"icmp type { echo-request, echo-reply } accept"
"iifname int jump input-ip4-lan"
"iifname ppp0 jump input-ip4-wan"
"iifname ppp0 jump incoming-allowed-ip4"
"ct state established,related accept"
"log prefix \"DENIED CHAIN=input-ip4 \""
];
};
forward-ip4 = {
type = "filter";
family = "ip";
policy = "drop";
hook = "forward";
rules = [
"iifname \"int\" accept"
"ct state established,related accept"
"oifname \"int\" iifname \"ppp0\" jump incoming-allowed-ip4"
"log prefix \"DENIED CHAIN=forward-ip4 \""
];
};
incoming-allowed-ip4 = {
type = "filter";
family = "ip";
rules = [
# This is where you put permitted incoming connections. If
# you're using NAT and want to forward a port from outside to
# devices on the LAN, then you need a DNAT rule in nat-rx chain
# *and* to accept the packet in this chain (specifying the
# internal (RFC1918) address).
];
};
}

View File

@ -8,36 +8,42 @@
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
inherit (pkgs.liminix.services) oneshot;
kconf = isModule :
# setting isModule false is utterly untested and mostly
# unimplemented: I say this to preempt any "how on earth is this
# even supposed to work?" questions
let yes = if isModule then "m" else "y";
in {
NFT_FIB_IPV4 = yes;
NFT_FIB_IPV6 = yes;
NF_TABLES = yes;
NF_CT_PROTO_DCCP = "y";
NF_CT_PROTO_SCTP = "y";
NF_CT_PROTO_UDPLITE = "y";
# NF_CONNTRACK_FTP = yes;
NFT_CT = yes;
};
kmodules = pkgs.kernel-modules.override {
kernelSrc = config.system.outputs.kernel.src;
modulesupport = config.system.outputs.kernel.modulesupport;
kmodules = pkgs.kmodloader.override {
inherit (config.system.outputs) kernel;
targets = [
"nft_fib_ipv4"
"nft_fib_ipv6"
"nf_log_syslog"
"ip6_tables"
"ip_tables"
"iptable_nat"
"nf_conntrack"
"nf_defrag_ipv4"
"nf_defrag_ipv6"
"nf_log_syslog"
"nf_nat"
"nf_reject_ipv4"
"nf_reject_ipv6"
"nf_tables"
"nft_chain_nat"
"nft_ct"
"nft_fib"
"nft_fib_ipv4"
"nft_fib_ipv6"
"nft_log"
"nft_masq"
"nft_nat"
"nft_reject"
"nft_reject_inet"
"nft_reject_ipv4"
"nft_reject_ipv6"
"x_tables"
"xt_MASQUERADE"
"xt_nat"
"xt_tcpudp"
];
kconfig = kconf true;
};
loadModules = oneshot {
name = "firewall-modules";
up = "sh ${kmodules}/load.sh";
down = "sh ${kmodules}/unload.sh";
};
in
{
@ -48,45 +54,57 @@ in
};
config = {
system.service.firewall =
let svc = liminix.callService ./service.nix {
ruleset = mkOption {
let svc = config.system.callService ./service.nix {
extraRules = mkOption {
type = types.attrsOf types.attrs;
description = "firewall ruleset";
default = {};
};
rules = mkOption {
type = types.attrsOf types.attrs; # we could usefully tighten this a bit :-)
default = import ./default-rules.nix;
description = "firewall ruleset";
};
};
in svc // {
build = args : (svc.build args) // {
dependencies = [ loadModules ] ++ (svc.dependencies or []);
};
build = args :
let args' = args // {
dependencies = (args.dependencies or []) ++ [kmodules];
};
in svc.build args' ;
};
# For historical reasons the kernel config is split between
# monolithic options and modules. TODO: go through this list
# and see what can be moved into the "kconf" definiton above
programs.busybox.applets = [
"insmod" "rmmod"
];
kernel.config = {
NETFILTER_XT_MATCH_CONNTRACK = "y";
IP6_NF_IPTABLES= "y";
IP_NF_IPTABLES= "y";
IP_NF_NAT = "y";
IP_NF_TARGET_MASQUERADE = "y";
NETFILTER = "y";
NETFILTER_ADVANCED = "y";
NETFILTER_XTABLES = "y";
NETFILTER_NETLINK = "m";
NF_CONNTRACK = "m";
NFT_COMPAT = "y";
NFT_CT = "y";
NFT_LOG = "y";
NFT_MASQ = "y";
NFT_NAT = "y";
NFT_REJECT = "y";
NFT_REJECT_INET = "y";
NETLINK_DIAG = "y";
NF_CONNTRACK = "y";
NF_NAT = "y";
NF_NAT_MASQUERADE = "y";
NF_TABLES= "y";
IP6_NF_IPTABLES= "m";
IP_NF_IPTABLES = "m";
IP_NF_NAT = "m";
IP_NF_TARGET_MASQUERADE = "m";
NFT_CT = "m";
NFT_FIB_IPV4 = "m";
NFT_FIB_IPV6 = "m";
NFT_LOG = "m";
NFT_MASQ = "m";
NFT_NAT = "m";
NFT_REJECT = "m";
NFT_REJECT_INET = "m";
NF_CT_PROTO_DCCP = "y";
NF_CT_PROTO_SCTP = "y";
NF_CT_PROTO_UDPLITE = "y";
NF_LOG_SYSLOG = "m";
NF_NAT = "m";
NF_NAT_MASQUERADE = "y";
NF_TABLES = "m";
NF_TABLES_INET = "y";
NF_TABLES_IPV4 = "y";
NF_TABLES_IPV6 = "y";

View File

@ -4,12 +4,10 @@
, firewallgen
, nftables
}:
{ ruleset }:
{ rules, extraRules }:
let
inherit (liminix.services) oneshot;
inherit (liminix.lib) typeChecked;
inherit (lib) mkOption types;
script = firewallgen "firewall.nft" ruleset;
script = firewallgen "firewall.nft" (lib.recursiveUpdate rules extraRules);
in oneshot {
name = "firewall";
up = script;

View File

@ -5,15 +5,20 @@
## you want to run on it, and would usually be set in the "device" file:
## :file:`devices/manuf-model/default.nix`
{ lib, pkgs, config, ...}:
{ lib, ... }:
let
inherit (lib) mkEnableOption mkOption types isDerivation hasAttr ;
in {
inherit (lib) mkOption types;
in
{
options = {
boot = {
};
boot = { };
hardware = {
ubi = {
minIOSize = mkOption { type = types.str; };
logicalEraseBlockSize = mkOption { type = types.str; }; # LEB
physicalEraseBlockSize = mkOption { type = types.str; }; # PEB
maxLEBcount = mkOption { type = types.str; }; # LEB
};
dts = {
src = mkOption {
type = types.nullOr types.path;
@ -25,11 +30,16 @@ in {
only for QEMU.
'';
};
includes = mkOption {
default = [];
includePaths = mkOption {
default = [ ];
description = "List of directories to search for DTS includes (.dtsi files)";
type = types.listOf types.path;
};
includes = mkOption {
default = [ ];
description = "\"dtsi\" fragments to include in the generated device tree";
type = types.listOf types.path;
};
};
defaultOutput = mkOption {
description = "\"Default\" output: what gets built for this device when outputs.default is requested. Typically this is \"mtdimage\" or \"vmroot\"";
@ -67,6 +77,7 @@ 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
@ -76,7 +87,11 @@ in {
default = [];
example = ["ath9k" "ath10k"];
};
rootDevice = mkOption { };
rootDevice = mkOption {
description = "Full path to preferred root device";
type = types.str;
example = "/dev/mtdblock3";
};
networkInterfaces = mkOption {
type = types.attrsOf types.anything;
};

View File

@ -0,0 +1,43 @@
## Health check
##
## Runs a service and a separate periodic health process. When the
## health check starts failing over a period of time, kill the service.
## (Usually that means the supervisor will restart it, but you can
## have other behaviours by e.g. combining this service with a round-robin
## for failover)
{ lib, pkgs, config, ...}:
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
# inherit (pkgs.liminix.services) longrun;
in {
options = {
system.service.health-check = mkOption {
description = "run a service while periodically checking it is healthy";
type = liminix.lib.types.serviceDefn;
};
};
config.system.service.health-check = config.system.callService ./service.nix {
service = mkOption {
type = liminix.lib.types.service;
};
interval = mkOption {
description = "interval between checks, in seconds";
type = types.int;
default = 10;
example = 10;
};
threshold = mkOption {
description = "number of consecutive failures required for the service to be kicked";
type = types.int;
example = 3;
};
healthCheck = mkOption {
description = "health check command or script. Expected to exit 0 if the service is healthy or any other exit status otherwise";
type = types.path;
};
};
config.programs.busybox.applets = ["expr"];
}

View File

@ -0,0 +1,37 @@
{
liminix, lib, lim, s6
}:
{ service, interval, threshold, healthCheck } :
let
inherit (liminix.services) oneshot longrun;
inherit (builtins) toString;
inherit (service) name;
checker = let name' = "check-${name}"; in longrun {
name = name';
run = ''
fails=0
echo waiting for /run/service/${name}
${s6}/bin/s6-svwait -U /run/service/${name} || exit
while sleep ${toString interval} ; do
${healthCheck}
if test $? -gt 0; then
fails=$(expr $fails + 1)
else
fails=0
fi
echo fails $fails/${toString threshold} for ${name}
if test "$fails" -gt "${toString threshold}" ; then
echo time to die
${s6}/bin/s6-svc -r /run/service/${name}
echo bounced
fails=0
echo waiting for /run/service/${name}
${s6}/bin/s6-svwait -U /run/service/${name}
fi
done
'';
};
in service.overrideAttrs(o: {
buildInputs = (lim.orEmpty o.buildInputs) ++ [ checker ];
dependencies = (lim.orEmpty o.dependencies) ++ [ checker ];
})

View File

@ -16,13 +16,14 @@ let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
in {
imports = [ ../secrets ];
options = {
system.service.hostapd = mkOption {
type = liminix.lib.types.serviceDefn;
};
};
config = {
system.service.hostapd = liminix.callService ./service.nix {
system.service.hostapd = config.system.callService ./service.nix {
interface = mkOption {
type = liminix.lib.types.service;
};

View File

@ -1,15 +1,16 @@
{
liminix
, svc
, hostapd
, output-template
, writeText
, lib
}:
{ interface, params} :
let
inherit (liminix.services) longrun;
inherit (lib) concatStringsSep mapAttrsToList;
inherit (liminix.lib) typeChecked;
inherit (lib) mkOption types;
inherit (lib) concatStringsSep mapAttrsToList unique ;
inherit (builtins) map filter attrValues length head typeOf;
# This is not a friendly interface to configuring a wireless AP: it
# just passes everything straight through to the hostapd config.
@ -22,18 +23,35 @@ let
driver = "nl80211";
logger_syslog = "-1";
logger_syslog_level = 1;
ctrl_interface = "/run/hostapd";
ctrl_interface = "/run/${name}";
ctrl_interface_group = 0;
};
attrs = defaults // params ;
literal_or_output = o: ({
string = builtins.toJSON;
int = builtins.toJSON;
lambda = (o: "output(${builtins.toJSON (o "service")}, ${builtins.toJSON (o "path")})");
}.${builtins.typeOf o}) o;
conf = writeText "hostapd.conf"
(concatStringsSep
"\n"
(mapAttrsToList
(name: value: "${name}=${toString value}")
(defaults // params)));
in longrun {
inherit name;
dependencies = [ interface ];
run = "${hostapd}/bin/hostapd -i $(output ${interface} ifname) -P /run/${name}.pid -S ${conf}";
conf =
(writeText "hostapd.conf.in"
((concatStringsSep
"\n"
(mapAttrsToList
(n : v : "${n}={{ ${literal_or_output v} }}")
attrs)) + "\n"));
service = longrun {
inherit name;
dependencies = [ interface ];
run = ''
mkdir -p /run/${name}
chmod 0700 /run/${name}
${output-template}/bin/output-template '{{' '}}' < ${conf} > /run/${name}/hostapd.conf
exec ${hostapd}/bin/hostapd -i $(output ${interface} ifname) -P /run/${name}/hostapd.pid -S /run/${name}/hostapd.conf
'';
};
watch = filter (f: typeOf f == "lambda") (attrValues attrs);
in svc.secrets.subscriber.build {
inherit service watch;
action = "restart-all";
}

View File

@ -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;
};
};
}

16
modules/ifwait/ifwait.nix Normal file
View File

@ -0,0 +1,16 @@
{ 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}
'';
}

View File

@ -5,18 +5,24 @@
{ lib, pkgs, config, ...}:
let
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;
inherit (lib) mkOption types ;
inherit (pkgs) liminix openwrt;
mergeConditionals = conf : conditions :
# for each key in conditions, if it is present in conf
# then merge the associated value into conf
lib.foldlAttrs
(acc: name: value:
if (conf ? ${name}) && (conf.${name} != "n")
then acc // value
else acc)
conf
conditions;
in {
options = {
kernel = {
src = mkOption { type = types.path; } ;
src = mkOption { type = types.path; default = openwrt.kernelSrc; } ;
version = mkOption { type = types.str; default = openwrt.kernelVersion;} ;
modular = mkOption {
type = types.bool;
default = true;
@ -42,20 +48,35 @@ in {
};
'';
};
conditionalConfig = mkOption {
description = ''
Kernel config options that should only be applied when
some other option is present.
'';
type = types.attrsOf (types.attrsOf types.nonEmptyStr);
default = {};
example = {
USB = {
USB_XHCI_MVEBU = "y";
USB_XHCI_HCD = "y";
};
};
};
makeTargets = mkOption {
type = types.listOf types.str;
};
};
};
config = {
system.outputs =
let k = liminix.builders.kernel.override {
inherit (config.kernel) config src extraPatchPhase;
targets = config.kernel.makeTargets;
};
in {
kernel = k.vmlinux;
zimage = k.zImage;
system.outputs.kernel =
let
mergedConfig = mergeConditionals
config.kernel.config
config.kernel.conditionalConfig;
in liminix.builders.kernel.override {
config = mergedConfig;
inherit (config.kernel) version src extraPatchPhase;
targets = config.kernel.makeTargets;
};
kernel = rec {
@ -66,9 +87,8 @@ in {
IKCONFIG_PROC = "y";
PROC_FS = "y";
KEXEC = "y";
MODULES = if modular then "y" else "n";
MODULE_SIG = if modular then "y" else "n";
MODULE_UNLOAD = if modular then "y" else "n";
DEBUG_FS = "y";
# basic networking protocols

15
modules/klogd.nix Normal file
View File

@ -0,0 +1,15 @@
{ config, lib, pkgs, ... }:
let
inherit (pkgs.liminix.services) longrun;
in {
config.services.klogd = longrun {
name = "klogd";
run = ''
echo "1 2 1 8" > /proc/sys/kernel/printk
cat /proc/kmsg
'';
finish = ''
echo "8 4 1 8" > /proc/sys/kernel/printk
'';
};
}

32
modules/mdevd.nix Normal file
View File

@ -0,0 +1,32 @@
{ 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;
timeout-up = 60 * 1000;
run = "exec ${pkgs.devout}/bin/devout /run/devout.sock 4";
dependencies = [ mdevd ];
};
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
];
};
};
};
}

View File

@ -7,11 +7,6 @@
let
inherit (lib) mkOption types;
inherit (pkgs) liminix;
mkBoolOption = description : mkOption {
type = types.bool;
inherit description;
default = true;
};
in {
options = {
@ -19,28 +14,39 @@ in {
type = liminix.lib.types.serviceDefn;
};
};
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";
};
imports = [ ../mdevd.nix ../uevent-rule ];
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.programs.busybox = {
applets = ["blkid" "findfs"];
options = {

View File

@ -1,18 +1,27 @@
{
liminix
, lib
, svc
}:
{ device, mountpoint, options, fstype }:
{ partlabel, 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 {
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}
'';
inherit name;
timeout-up = 3600;
up = "mount -t ${fstype} ${options_string} ${device} ${mountpoint}";
down = "umount ${mountpoint}";
inherit controller;
}

View File

@ -1,6 +1,5 @@
{
liminix
, ifwait
, serviceFns
, lib
}:
@ -12,7 +11,6 @@ let
# prefixes, or the same but different protocols
name = "${interface.name}.a.${address}";
up = ''
. ${serviceFns}
dev=$(output ${interface} ifname)
ip address add ${address}/${toString prefixLength} dev $dev
(in_outputs ${name}

View File

@ -64,7 +64,7 @@ in {
services.loopback = config.hardware.networkInterfaces.lo;
system.service.network = {
link = liminix.callService ./link.nix {
link = config.system.callService ./link.nix {
ifname = mkOption {
type = types.str;
example = "eth0";
@ -89,7 +89,7 @@ in {
example = 1480;
};
};
address = liminix.callService ./address.nix {
address = config.system.callService ./address.nix {
interface = mkOption {
type = liminix.lib.types.service;
};
@ -104,7 +104,7 @@ in {
};
};
route = liminix.callService ./route.nix {
route = config.system.callService ./route.nix {
interface = mkOption {
type = types.nullOr liminix.lib.types.interface;
default = null;
@ -125,7 +125,7 @@ in {
};
};
forward = liminix.callService ./forward.nix {
forward = config.system.callService ./forward.nix {
enableIPv4 = mkOption {
type = types.bool;
default = true;
@ -136,7 +136,7 @@ in {
};
};
dhcp.client = liminix.callService ./dhcpc.nix {
dhcp.client = config.system.callService ./dhcpc.nix {
interface = mkOption {
type = liminix.lib.types.service;
};

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 > $i
(printenv $i || true) > $i
done)
}
case $action in
@ -40,7 +40,7 @@ let
'';
in longrun {
inherit name;
run = "/bin/udhcpc -f -i $(output ${interface} ifname) -x hostname:$(cat /proc/sys/kernel/hostname) -s ${script}";
run = "exec /bin/udhcpc -f -i $(output ${interface} ifname) -x hostname:$(cat /proc/sys/kernel/hostname) -s ${script}";
notification-fd = 10;
dependencies = [ interface ];
}

View File

@ -1,7 +1,5 @@
{
liminix
, ifwait
, serviceFns
, lib
}:
{ enableIPv4, enableIPv6 }:

View File

@ -1,7 +1,5 @@
{
liminix
, ifwait
, serviceFns
, lib
}:
{
@ -11,8 +9,7 @@
# if devpath is supplied, we rename the interface at that
# path to have the specified name.
let
inherit (liminix.services) longrun oneshot;
inherit (lib) concatStringsSep;
inherit (liminix.services) oneshot;
name = "${ifname}.link";
rename = if devpath != null
then ''

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}-${builtins.substring 0 12 (builtins.hashString "sha256" "${via}-${if interface!=null then interface.name else ""}")}";
name = "route-${target_hash}-${builtins.substring 0 12 (builtins.hashString "sha256" "${via_hash}-${if interface!=null then interface.name else ""}")}";
up = ''
ip route add ${target} via ${via} metric ${toString metric} ${with_dev}
'';

View File

@ -18,7 +18,7 @@ in {
};
};
config = {
system.service.ntp = liminix.callService ./service.nix {
system.service.ntp = config.system.callService ./service.nix {
user = mkOption {
type = types.str;
default = "ntp";

View File

@ -1,18 +1,14 @@
{
liminix
, chrony
, serviceFns
, lib
, writeText
}:
params:
let
name = "ntp"; # bad name, needs to be unique
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)
@ -28,11 +24,15 @@ let
++ (lib.optional (p.bindaddress != null) "bindaddress ${p.bindaddress}")
++ (lib.optional (p.binddevice != null) "binddevice ${p.binddevice}")
++ (lib.optional (p.dumpdir != null) "dumpdir ${p.dumpdir}")
++ [
"bindcmdaddress /" # disable unix socket
"pidfile /run/${name}.pid"
]
++ [p.extraConfig];
config = writeText "chrony.conf"
(concatStringsSep "\n" (configFile params));
in longrun {
name = "ntp"; # bad name, needs to be unique
inherit name;
run = "${chrony}/bin/chronyd -f ${config} -d";
}

View File

@ -1,19 +1,24 @@
{
config
, pkgs
, lib
, ...
config,
pkgs,
lib,
...
}:
let
inherit (lib) mkOption types concatStringsSep;
inherit (pkgs) liminix callPackage writeText;
inherit (pkgs) liminix writeText;
o = config.system.outputs;
in
{
imports = [
./squashfs.nix
./outputs/squashfs.nix
./outputs/jffs2.nix
./outputs/vmroot.nix
./outputs/extlinux.nix
./outputs/boot-extlinux.nix
./outputs/boot-fit.nix
./outputs/uimage.nix
./outputs/updater
./outputs/ubimage.nix
];
options = {
system.outputs = {
@ -22,27 +27,17 @@ in
# but only part of one.
kernel = mkOption {
type = types.package;
internal = true;
internal = true;
description = ''
kernel
******
Kernel vmlinux file (usually ELF)
'';
};
zimage = mkOption {
type = types.package;
internal = true;
description = ''
zimage
******
Kernel in compressed self-extracting package
Kernel package (multi-output derivation)
'';
};
dtb = mkOption {
type = types.package;
internal = true;
internal = true;
description = ''
dtb
***
@ -50,22 +45,15 @@ in
Compiled device tree (FDT) for the target device
'';
};
uimage = mkOption {
tplink-safeloader = mkOption {
type = types.package;
internal = true;
description = ''
uimage
******
Combined kernel and FDT in uImage (U-Boot compatible) format
'';
};
u-boot = mkOption {
type = types.package;
};
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
@ -79,6 +67,12 @@ in
directory of files to package into root filesystem
'';
};
bootfiles = mkOption {
type = types.nullOr types.package;
internal = true;
default = null;
# description = "";
};
bootablerootdir = mkOption {
type = types.package;
internal = true;
@ -101,17 +95,11 @@ in
system.outputs = rec {
dtb = liminix.builders.dtb {
inherit (config.boot) commandLine;
dts = config.hardware.dts.src;
includes = config.hardware.dts.includes ++ [
dts = [config.hardware.dts.src] ++ config.hardware.dts.includes;
includes = config.hardware.dts.includePaths ++ [
"${o.kernel.headers}/include"
];
};
uimage = liminix.builders.uimage {
commandLine = concatStringsSep " " config.boot.commandLine;
inherit (config.hardware) loadAddress entryPoint;
inherit (config.boot) imageFormat;
inherit (o) kernel dtb;
};
rootdir =
let
inherit (pkgs.pkgsBuildBuild) runCommand;
@ -128,8 +116,8 @@ in
let inherit (pkgs.pkgsBuildBuild) runCommand;
in runCommand "add-slash-boot" { } ''
cp -a ${o.rootdir} $out
${if config.boot.loader.extlinux.enable
then "(cd $out && chmod -R +w . && rmdir boot && cp -a ${o.extlinux} boot)"
${if o.bootfiles != null
then "(cd $out && chmod -R +w . && rmdir boot && cp -a ${o.bootfiles} boot)"
else ""
}
'';

View File

@ -6,34 +6,34 @@
}:
let
inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep;
inherit (pkgs.pseudofile) dir symlink;
cfg = config.boot.loader.extlinux;
o = config.system.outputs;
cmdline = concatStringsSep " " config.boot.commandLine;
wantsDtb = config.hardware.dts ? src && config.hardware.dts.src != null;
in {
options.system.outputs.extlinux = mkOption {
type = types.package;
# description = "";
};
options.boot.loader.extlinux.enable = mkEnableOption "extlinux";
config = { # mkIf cfg.enable {
system.outputs.extlinux = pkgs.runCommand "extlinux" {} ''
config = mkIf cfg.enable {
system.outputs.bootfiles = pkgs.runCommand "extlinux" {} ''
mkdir $out
cd $out
${if wantsDtb then "cp ${o.dtb} dtb" else "true"}
cp ${o.initramfs} initramfs
cp ${o.zimage} kernel
cp ${o.kernel.zImage} kernel
mkdir extlinux
cat > extlinux/extlinux.conf << _EOF
menu title Liminix
timeout 100
timeout 40
label Liminix
kernel /boot/kernel
# initrd /boot/initramfs
append ${cmdline} root=/dev/vda1
append ${cmdline}
${if wantsDtb then "fdt /boot/dtb" else ""}
_EOF
'';
filesystem = dir {
boot = symlink config.system.outputs.bootfiles;
};
};
}

View File

@ -0,0 +1,27 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkIf mkEnableOption mkOption types concatStringsSep;
inherit (pkgs.pseudofile) dir symlink;
cfg = config.boot.loader.fit;
o = config.system.outputs;
cmdline = concatStringsSep " " config.boot.commandLine;
wantsDtb = config.hardware.dts ? src && config.hardware.dts.src != null;
in {
options.boot.loader.fit.enable = mkEnableOption "FIT in /boot";
config = mkIf cfg.enable {
system.outputs.bootfiles = pkgs.runCommand "boot-fit" {} ''
mkdir $out
cd $out
cp ${o.uimage} fit
'';
filesystem = dir {
boot = symlink config.system.outputs.bootfiles;
};
};
}

37
modules/outputs/btrfs.nix Normal file
View File

@ -0,0 +1,37 @@
{
config
, pkgs
, lib
, ...
}:
let
inherit (lib) mkIf;
o = config.system.outputs;
in
{
imports = [
./initramfs.nix
];
config = mkIf (config.rootfsType == "btrfs") {
kernel.config = {
BTRFS_FS = "y";
};
boot.initramfs.enable = true;
system.outputs = {
rootfs =
let
inherit (pkgs.pkgsBuildBuild) runCommand e2fsprogs;
in runCommand "mkfs.btrfs" {
depsBuildBuild = [ e2fsprogs ];
} ''
tree=${o.bootablerootdir}
size=$(du -s --apparent-size --block-size 1024 $tree |cut -f1)
# add 25% for filesystem overhead
size=$(( 5 * $size / 4))
dd if=/dev/zero of=$out bs=1024 count=$size
echo "not implemented" ; exit 1
# mke2fs -t ext4 -j -d $tree $out
'';
};
};
}

View File

@ -5,7 +5,7 @@
, ...
}:
let
inherit (lib) mkIf mkOption types;
inherit (lib) mkIf;
o = config.system.outputs;
in
{

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