add build-host tftp server

This commit is contained in:
Daniel Barlow 2022-10-04 23:08:43 +01:00
parent f9626d00f4
commit e7987c9520
5 changed files with 114 additions and 0 deletions

View file

@ -116,6 +116,22 @@ Assuming you have nixpkgs checked out in a peer directory of this one,
Some of the tests require the emulated upstream connection to be running. Some of the tests require the emulated upstream connection to be running.
## Hardware
How you get the thing onto hardware will vary according to the device,
but is likely to involve U-Boot and TFTP.
There is a rudimentary TFTP server bundled with the system which runs
from the command line, has an allowlist for client connections, and
follows symlinks, so you can have your device download images direct
from the `./result` directory without exposing `/nix/store/` to the
internet or mucking about copying files to `/tftproot`. If the
permitted device is to be given the IP address 192.168.8.251 you might
do something like this:
$ NIX_PATH=nixpkgs=../nixpkgs:$NIX_PATH NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM=1 nix-build -I liminix-config=./tests/smoke/configuration.nix --arg device "import ./devices/gl-ar750.nix" -A outputs.tftpd -o tftpd
$ ./tftpd/bin/tufted -a 192.168.8.251 result
## Troubleshooting ## Troubleshooting

View file

@ -39,6 +39,7 @@ let
# this exists so that you can run "nix-store -q --tree" on it and find # this exists so that you can run "nix-store -q --tree" on it and find
# out what's in the image, which is nice if it's unexpectedly huge # out what's in the image, which is nice if it's unexpectedly huge
manifest = writeText "manifest.json" (builtins.toJSON config.filesystem.contents); manifest = writeText "manifest.json" (builtins.toJSON config.filesystem.contents);
tftpd = nixpkgs.pkgs.buildPackages.tufted;
}; };
in { in {
outputs = outputs // { default = outputs.${device.outputs.default}; }; outputs = outputs // { default = outputs.${device.outputs.default}; };

View file

@ -16,6 +16,8 @@ final: prev: {
dbusSupport = false; dbusSupport = false;
}; };
tufted = final.callPackage ./pkgs/tufted {};
pppoe = final.callPackage ./pkgs/pppoe {}; pppoe = final.callPackage ./pkgs/pppoe {};
ppp = ppp =
(prev.ppp.override { (prev.ppp.override {

44
pkgs/tufted/default.nix Normal file
View file

@ -0,0 +1,44 @@
{
lua5_3
, stdenv
, fennel
, fetchFromGitHub
, makeWrapper
} :
let
tufty-lua = lua.pkgs.buildLuaPackage {
pname = "tufty";
version = "1";
src = fetchFromGitHub {
owner = "telent";
repo = "tufty";
sha256 = "sha256-m5UEfcCNdG0Ku380cPhu1inNQmSfQJ5NcRIxLohUOh8=";
rev = "75c6d38713a82f4197f91dcb182a2e34f255bf7c";
};
buildPhase = ":";
installPhase = ''
mkdir -p "$out/share/lua/${lua5_3.luaversion}"
cp src/*.lua "$out/share/lua/${lua5_3.luaversion}/"
'';
};
lua = lua5_3.withPackages (ps: with ps; [
tufty-lua luasocket luaposix
]);
fennel_ = (fennel.override { inherit lua; });
in stdenv.mkDerivation {
pname = "tufted";
version = "1";
phases = [ "unpackPhase" "installPhase" ];
buildInputs = [ lua fennel_ ];
nativeBuildInputs = [ makeWrapper ];
src = ./.;
installPhase = ''
mkdir -p $out/lib
cp tufted.fnl $out/lib
makeWrapper ${fennel_}/bin/fennel \
$out/bin/tufted \
--add-flags "--add-fennel-path $out/lib/?.fnl" \
--add-flags "--add-package-path $out/lib/?.lua" \
--add-flags "$out/lib/tufted.fnl"
'';
}

51
pkgs/tufted/tufted.fnl Normal file
View file

@ -0,0 +1,51 @@
(local tftp (require :tftp))
(local { : realpath} (require :posix.stdlib))
(local { : view } (require :fennel))
(local options
(match arg
["-a" ip-address dir]
{ :allow ip-address :base-directory (realpath dir)}
[dir]
{ :allow nil :base-directory (realpath dir)}
[]
(error "missing command line parameters")
))
(print (.. "TFTP serving from " options.base-directory))
(fn merge-pathname [directory filename]
(if (directory:match "/$")
(.. directory filename)
(.. directory "/" filename)))
(->
(tftp:listen
(fn [file host port]
(if (or (not options.allow) (= host options.allow))
(let [pathname (merge-pathname options.base-directory file)
f (io.open pathname "rb")
size (f:seek "end")]
(f:seek "set" 0)
(var eof? false)
(values
(fn handler [reqlen]
(let [bytes (f:read reqlen)]
(if eof?
false
bytes
(values true bytes)
(do
;; if the file length is divisible by the block
;; length, need to send an empty block at eof
(set eof? true)
(values true "")))))
size))
(error "host not allowed")))
nil
["*"]
69)
(os.exit))