diff --git a/.github/workflows/app-dnsmasq.yml b/.github/workflows/app-dnsmasq.yml new file mode 100644 index 00000000..f58655fb --- /dev/null +++ b/.github/workflows/app-dnsmasq.yml @@ -0,0 +1,68 @@ +name: app-dnsmasq + + +on: + workflow_dispatch: + push: + paths: + - 'dnsmasq/**' + - '.github/workflows/app-dnsmasq.yml' + +jobs: + dnsmasq: + strategy: + fail-fast: false + matrix: + include: + - arch: x86_64 + runner: ubuntu-latest + qemu: qemu-system-x86 + # arm64 is build-only (all.sh skips boot+test) + - arch: arm64 + runner: ubuntu-26.04-arm + qemu: qemu-system-arm + runs-on: ${{ matrix.runner }} + timeout-minutes: 40 + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + build-essential libncurses-dev libyaml-dev flex bison \ + git wget curl uuid-runtime \ + ${{ matrix.qemu }} libarchive-tools dnsutils dns-root-data + + - name: Clone dnsmasq dependencies into repos/ + run: | + + # lib-nettle/lib-dnsmasq: feature-branch forks until upstreamed. + test -d repos/unikraft || git clone --depth 1 https://github.com/unikraft/unikraft repos/unikraft + test -d repos/libs/lwip || git clone --depth 1 https://github.com/unikraft/lib-lwip repos/libs/lwip + test -d repos/libs/musl || git clone --depth 1 https://github.com/unikraft/lib-musl repos/libs/musl + test -d repos/libs/nettle || git clone --depth 1 -b musl-hogweed-dnssec https://github.com/vTusharr/lib-nettle repos/libs/nettle + test -d repos/libs/dnsmasq || git clone --depth 1 -b dnsmasq-port https://github.com/vTusharr/lib-dnsmasq repos/libs/dnsmasq + + - name: Build and test dnsmasq (${{ matrix.arch }}) + run: | + cd dnsmasq + ./.scripts/test/all.sh qemu.${{ matrix.arch }} | tee output.log + if grep -q FAILED output.log; then exit 1; fi + + - name: Dump logs on failure + if: failure() + run: | + for f in dnsmasq/.scripts/test/log/*; do + echo "===== $f ====="; cat "$f"; echo + done + + - name: Upload logs + # act cannot mint an artifact upload token so skip + if: ${{ always() && !env.ACT }} + uses: actions/upload-artifact@v4 + with: + name: dnsmasq-test-logs-${{ matrix.arch }} + path: dnsmasq/.scripts/test/log/* + retention-days: 7 diff --git a/dnsmasq/.gitignore b/dnsmasq/.gitignore new file mode 100644 index 00000000..7c374a1c --- /dev/null +++ b/dnsmasq/.gitignore @@ -0,0 +1,8 @@ +/workdir/ +/rootfs/etc/trust-anchors.conf +/rootfs/etc/dnsmasq.conf.bak +*.cpio +*.log +*.pid +.config +.config.old diff --git a/dnsmasq/.scripts/build/qemu.arm64 b/dnsmasq/.scripts/build/qemu.arm64 new file mode 100755 index 00000000..0b9ae798 --- /dev/null +++ b/dnsmasq/.scripts/build/qemu.arm64 @@ -0,0 +1,9 @@ +#!/bin/sh + +export UK_CFLAGS="${UK_CFLAGS:--std=gnu17 -w}" + +make distclean +UK_DEFCONFIG=$(pwd)/.scripts/defconfig/qemu.arm64 make defconfig +touch Makefile.uk +make prepare +make -j $(nproc) diff --git a/dnsmasq/.scripts/build/qemu.x86_64 b/dnsmasq/.scripts/build/qemu.x86_64 new file mode 100755 index 00000000..11344ece --- /dev/null +++ b/dnsmasq/.scripts/build/qemu.x86_64 @@ -0,0 +1,9 @@ +#!/bin/sh + +export UK_CFLAGS="${UK_CFLAGS:--std=gnu17 -w}" + +make distclean +UK_DEFCONFIG=$(pwd)/.scripts/defconfig/qemu.x86_64 make defconfig +touch Makefile.uk +make prepare +make -j $(nproc) diff --git a/dnsmasq/.scripts/defconfig/qemu.arm64 b/dnsmasq/.scripts/defconfig/qemu.arm64 new file mode 100644 index 00000000..5405b642 --- /dev/null +++ b/dnsmasq/.scripts/defconfig/qemu.arm64 @@ -0,0 +1,20 @@ +CONFIG_PLAT_KVM=y +CONFIG_LIBPOSIX_ENVIRON_LIBPARAM=y +CONFIG_LIBPOSIX_POLL_YIELD=y +CONFIG_LIBUKPRINT_KLVL_WARN=y +CONFIG_LIBUKPRINT_LOGBUF=y +CONFIG_LIBUKRANDOM_GETRANDOM=y +CONFIG_LIBUKRANDOM_DEVFS=y +CONFIG_LIBMUSL_NETWORK_LWIP_DNS=y +CONFIG_LIBLWIP=y +CONFIG_LWIP_TCP_KEEPALIVE=y +CONFIG_LWIP_NUM_TCPCON=256 +CONFIG_LWIP_NUM_TCPLISTENERS=256 +CONFIG_LWIP_DHCP=y +CONFIG_LWIP_WAITIFACE=y +CONFIG_OPTIMIZE_DEADELIM=y +CONFIG_ARCH_ARM_64=y +CONFIG_ARM64_ERRATUM_858921=n +CONFIG_ARM64_ERRATUM_835769=n +CONFIG_ARM64_ERRATUM_843419=n +CONFIG_UK_NAME="dnsmasq" diff --git a/dnsmasq/.scripts/defconfig/qemu.x86_64 b/dnsmasq/.scripts/defconfig/qemu.x86_64 new file mode 100644 index 00000000..0a53d969 --- /dev/null +++ b/dnsmasq/.scripts/defconfig/qemu.x86_64 @@ -0,0 +1,17 @@ +CONFIG_PLAT_KVM=y +CONFIG_LIBPOSIX_ENVIRON_LIBPARAM=y +CONFIG_LIBPOSIX_POLL_YIELD=y +CONFIG_LIBUKPRINT_KLVL_WARN=y +CONFIG_LIBUKPRINT_LOGBUF=y +CONFIG_LIBUKRANDOM_GETRANDOM=y +CONFIG_LIBUKRANDOM_DEVFS=y +CONFIG_LIBMUSL_NETWORK_LWIP_DNS=y +CONFIG_LIBLWIP=y +CONFIG_LWIP_TCP_KEEPALIVE=y +CONFIG_LWIP_NUM_TCPCON=256 +CONFIG_LWIP_NUM_TCPLISTENERS=256 +CONFIG_LWIP_DHCP=y +CONFIG_LWIP_WAITIFACE=y +CONFIG_OPTIMIZE_DEADELIM=y +CONFIG_ARCH_X86_64=y +CONFIG_UK_NAME="dnsmasq" diff --git a/dnsmasq/.scripts/run/qemu.arm64 b/dnsmasq/.scripts/run/qemu.arm64 new file mode 100755 index 00000000..d4728883 --- /dev/null +++ b/dnsmasq/.scripts/run/qemu.arm64 @@ -0,0 +1,45 @@ +#!/bin/sh +# Run the dnsmasq unikernel with an initrd (cpio) rootfs and SLIRP user +# networking (no root/bridge setup needed). +# Host port 5355 (UDP+TCP) -> VM port 53. +# +# The VM gets its address by DHCP from QEMU SLIRP's built-in server. For a +# static IP (e.g. on Firecracker) add before the +# "--" separator: +# netdev.ip=172.44.0.2/24:172.44.0.1::: +# +# Test: dig @127.0.0.1 -p 5355 test.local +# dig +tcp @127.0.0.1 -p 5355 test.local + +if test ! -f "workdir/build/dnsmasq_qemu-arm64"; then + echo "No kernel file workdir/build/dnsmasq_qemu-arm64." 1>&2 + echo "Did you run ./.scripts/build/qemu.arm64 ?" 1>&2 + exit 1 +fi + +# Root DNSSEC trust anchors: prefer the build host's dns-root-data (kept current +# by the host's package manager), fall back to pinned IANA values if absent. +if [ -f /usr/share/dnsmasq/trust-anchors.conf ]; then + cp /usr/share/dnsmasq/trust-anchors.conf rootfs/etc/trust-anchors.conf +else + cat > rootfs/etc/trust-anchors.conf <<'EOF' +trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D +trust-anchor=.,38696,8,2,683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16 +EOF +fi + +# Pack filesystem as an initial ramdisk CPIO file. +rm -f initrd.cpio +./workdir/unikraft/support/scripts/mkcpio initrd.cpio ./rootfs/ + +#run +exec qemu-system-aarch64 -m 128M \ + -machine virt \ + -cpu max \ + -nographic \ + -no-reboot \ + -kernel workdir/build/dnsmasq_qemu-arm64 \ + -initrd initrd.cpio \ + -device virtio-net,netdev=net0,romfile="" \ + -netdev user,id=net0,hostfwd=udp::5355-:53,hostfwd=tcp::5355-:53 \ + -append "dnsmasq vfs.fstab=[ \"initrd0:/:extract::ramfs=1:\" ] -- -d -C /etc/dnsmasq.conf" diff --git a/dnsmasq/.scripts/run/qemu.x86_64 b/dnsmasq/.scripts/run/qemu.x86_64 new file mode 100755 index 00000000..428135ce --- /dev/null +++ b/dnsmasq/.scripts/run/qemu.x86_64 @@ -0,0 +1,43 @@ +#!/bin/sh +# Run the dnsmasq unikernel with an initrd rootfs and slirp user +# networking (no root/bridge setup needed). +# Host port 5355 (UDP+TCP) -> VM port 53. +# +# The VM gets its address by DHCP from qemu slirp's built-in server. For a +# static IP (e.g. on Firecracker) add before the +# "--" separator: +# netdev.ip=172.44.0.2/24:172.44.0.1::: +# +# Test: dig @127.0.0.1 -p 5355 test.local +# dig +tcp @127.0.0.1 -p 5355 test.local + +if test ! -f "workdir/build/dnsmasq_qemu-x86_64"; then + echo "No kernel file workdir/build/dnsmasq_qemu-x86_64." 1>&2 + echo "Did you run ./.scripts/build/qemu.x86_64 ?" 1>&2 + exit 1 +fi + +# Root DNSSEC trust anchors: prefer the build host's dns-root-data (kept current +# by the host's package manager), fall back to pinned IANA values if absent. +if [ -f /usr/share/dnsmasq/trust-anchors.conf ]; then + cp /usr/share/dnsmasq/trust-anchors.conf rootfs/etc/trust-anchors.conf +else + cat > rootfs/etc/trust-anchors.conf <<'EOF' +trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D +trust-anchor=.,38696,8,2,683D2D0ACB8C9B712A1948B27F741219298D0A450D612C483AF444A4C0FB2B16 +EOF +fi + +# Pack filesystem as an initial ramdisk CPIO file. +rm -f initrd.cpio +./workdir/unikraft/support/scripts/mkcpio initrd.cpio ./rootfs/ + +exec qemu-system-x86_64 -m 128M \ + -cpu max \ + -nographic \ + -no-reboot \ + -kernel workdir/build/dnsmasq_qemu-x86_64 \ + -initrd initrd.cpio \ + -device virtio-net,netdev=net0 \ + -netdev user,id=net0,hostfwd=udp::5355-:53,hostfwd=tcp::5355-:53 \ + -append "vfs.fstab=[ \"initrd0:/:extract::ramfs=1:\" ] -- -d -C /etc/dnsmasq.conf" diff --git a/dnsmasq/.scripts/test/.gitignore b/dnsmasq/.scripts/test/.gitignore new file mode 100644 index 00000000..8297d08b --- /dev/null +++ b/dnsmasq/.scripts/test/.gitignore @@ -0,0 +1 @@ +/log/ diff --git a/dnsmasq/.scripts/test/all.sh b/dnsmasq/.scripts/test/all.sh new file mode 100755 index 00000000..df6f70f2 --- /dev/null +++ b/dnsmasq/.scripts/test/all.sh @@ -0,0 +1,48 @@ +#!/bin/sh +# Build and test the dnsmasq unikernel. +# Usage: all.sh [] e.g. all.sh qemu.x86_64 +# With no target, runs every target (x86_64 + arm64). + +test_build() +{ + printf "%-46s ... " build."$1" + ./.scripts/build/"$1" > ./.scripts/test/log/build."$1" 2>&1 + if test $? -eq 0; then + echo "PASSED" + else + echo "FAILED" + fi +} + +test_build_run() +{ + printf "%-46s ... " build."$1" + ./.scripts/build/"$1" > ./.scripts/test/log/build."$1" 2>&1 + if test $? -eq 0; then + echo "PASSED" + else + echo "FAILED" + fi + + printf " %-42s ... " run."$1" + ./.scripts/test/wrapper.sh ./.scripts/run/"$1" 2> ./.scripts/test/log/run."$1" +} + +# arm64 is build-only for now , DNSSEC validation fails and every +# forwarded query SERVFAILs : Re-enable with test_build_run once the clock works. +run_target() +{ + case "$1" in + *arm64*) test_build "$1" ;; + *) test_build_run "$1" ;; + esac +} + +./setup.sh +test -d ./.scripts/test/log || mkdir ./.scripts/test/log +if test -n "$1"; then + run_target "$1" +else + run_target qemu.x86_64 + run_target qemu.arm64 +fi diff --git a/dnsmasq/.scripts/test/common.sh b/dnsmasq/.scripts/test/common.sh new file mode 100755 index 00000000..137c9c68 --- /dev/null +++ b/dnsmasq/.scripts/test/common.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# helpers for the dnsmasq unikernel. The actual DNS checks +# live in the app's top-level test.sh . The instance +# runs under qemu slirp with host-forwarded ports, so queries go to 127.0.0.1 +# on the forwarded DNS port (default 5355 -> guest 53). + +host="${host:-127.0.0.1}" +port="${port:-5355}" + +clean_up() +{ + { + sudo pkill -9 qemu-system + pkill -9 qemu-system + } > /dev/null 2>&1 +} + +start_instance() +{ + setsid --fork "$start_command" 1>&2 & + if test $? -ne 0; then + echo "Cannot start instance" 1>&2 + echo "FAILED" + clean_up + exit 1 + fi +} + +# Wait until the forwarded dns port returns a real A record, or time out. +# Note:-> `dig +short` prints its "connection refused" diagnostic to stdout, so +# readiness must be detected by an actual answer (a line starting with a +# digit), not merely by non empty output. +wait_for_dns() +{ + i=0 + while test "$i" -lt 300; do + if dig +short +timeout=1 +tries=1 @"$host" -p "$port" test.local \ + 2>/dev/null | grep -qE '^[0-9]'; then + return 0 + fi + sleep 1 + i=$((i + 1)) + done + echo "dnsmasq did not become ready on $host:$port" 1>&2 + echo "FAILED" + clean_up + exit 1 +} + +end_with_success() +{ + echo "PASSED" + clean_up + exit 0 +} + +start_command="$1" diff --git a/dnsmasq/.scripts/test/wrapper.sh b/dnsmasq/.scripts/test/wrapper.sh new file mode 100755 index 00000000..35f01814 --- /dev/null +++ b/dnsmasq/.scripts/test/wrapper.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Start an instance, run a single round of DNS checks +# Usage: ./.scripts/test/wrapper.sh + +. ./.scripts/test/common.sh + +if test $# -lt 1; then + echo "Usage: $0 " 1>&2 + exit 1 +fi + +# Clean up +clean_up + +# Start instance (backgrounded via setsid in start_instance). +start_instance + +# Wait for readiness, then run the checks +wait_for_dns +if HOST="$host" PORT="$port" ./test.sh; then + end_with_success +else + echo "FAILED" + clean_up + exit 1 +fi diff --git a/dnsmasq/Config.uk b/dnsmasq/Config.uk new file mode 100644 index 00000000..66865a01 --- /dev/null +++ b/dnsmasq/Config.uk @@ -0,0 +1,26 @@ +# Configure dnsmasq application for a build that uses initrd to pass the +# root filesystem and user networking. + +config APPDNSMASQ +bool "Configure dnsmasq application with initrd as rootfs" +default y + + # Select application library (dnsmasq). Use main function in application + # library. Other libraries, such as musl or lwip, are selected as + # dependencies of dnsmasq. + select LIBDNSMASQ + select LIBDNSMASQ_MAIN_FUNCTION + + # Select fs core components: vfscore, cpio, ramfs, devfs. + select LIBVFSCORE + select LIBVFSCORE_AUTOMOUNT_UP + select LIBRAMFS + select LIBUKCPIO + select LIBDEVFS + select LIBDEVFS_AUTOMOUNT + select LIBDEVFS_DEVSTDOUT + + # Use extended information (einfo) for configuring network parameters. + # This component parses the configuration string in the command line: + # i.e netdev.ip=172.44.0.2/24:172.44.0.1::: + select LIBUKNETDEV_EINFO_LIBPARAM diff --git a/dnsmasq/Makefile b/dnsmasq/Makefile new file mode 100644 index 00000000..e391711b --- /dev/null +++ b/dnsmasq/Makefile @@ -0,0 +1,10 @@ +UK_ROOT ?= $(PWD)/workdir/unikraft +UK_LIBS ?= $(PWD)/workdir/libs/musl:$(PWD)/workdir/libs/lwip:$(PWD)/workdir/libs/nettle:$(PWD)/workdir/libs/dnsmasq +UK_BUILD ?= $(PWD)/workdir/build + +.PHONY: all +all: + @$(MAKE) -C $(UK_ROOT) A=$(PWD) L=$(UK_LIBS) O=$(UK_BUILD) + +$(MAKECMDGOALS): + @$(MAKE) -C $(UK_ROOT) A=$(PWD) L=$(UK_LIBS) O=$(UK_BUILD) $(MAKECMDGOALS) diff --git a/dnsmasq/Makefile.uk b/dnsmasq/Makefile.uk new file mode 100644 index 00000000..c5ccab60 --- /dev/null +++ b/dnsmasq/Makefile.uk @@ -0,0 +1 @@ +# Empty , all build logic is in the library diff --git a/dnsmasq/README.md b/dnsmasq/README.md new file mode 100644 index 00000000..f4a96df2 --- /dev/null +++ b/dnsmasq/README.md @@ -0,0 +1,80 @@ +# dnsmasq on Unikraft + +Run [dnsmasq 2.91](https://thekelleys.org.uk/dnsmasq/doc.html) as a DNS +forwarder/cache (UDP + TCP) with DNSSEC validation on by default. DHCP, TFTP are not supported they need `PF_PACKET`, layer 2 things ; + + +## Quick Setup (aka TLDR) + + + +For a quick setup, run the commands below. +Note that you still need to install the [requirements](../README.md#requirements). +Before everything, make sure you run the [top-level `setup.sh` script](../setup.sh). + + +```console +./setup.sh +./.scripts/build/qemu.x86_64 +./.scripts/run/qemu.x86_64 +``` + + + +The run script packs `rootfs/` into `initrd.cpio` (extracted to a ramfs on +boot) and starts QEMU with slirp user networking — no bridge or root needed. +Host port **5355** (UDP + TCP) forwards to VM port 53; the VM gets its address +from SLIRP's DHCP. For a static IP add `netdev.ip=172.44.0.2/24:172.44.0.1:::` +before the `--` separator in `-append`. Quit QEMU with `Ctrl-A then X`. + +## Test + +```console +./test.sh +``` + +or manually: + +```console +dig @127.0.0.1 -p 5355 test.local # UDP -> 172.44.0.2 +dig +tcp @127.0.0.1 -p 5355 test.local # TCP -> 172.44.0.2 +dig @127.0.0.1 -p 5355 unikraft.com # forwarding + cache +dig @127.0.0.1 -p 5355 +dnssec cloudflare.com # 'ad' flag = validated +dig @127.0.0.1 -p 5355 dnssec-failed.org # SERVFAIL = validation works +``` + +## Configuration + +`rootfs/etc/dnsmasq.conf` is commented; the Unikraft-specific must-haves: + +| Option | Why | +|--------|-----| +| `no-daemon` (+ `-d` flag) | Single process, foreground | +| `log-facility=-` | Log to stdout — no syslog | +| `user=root` | `getpwnam()` only knows `root` (no `/etc/passwd`); the default privilege drop to `nobody` is fatal | +| `bind-interfaces` | Bind discovered addresses instead of wildcard | + +The rest is normal dnsmasq tuning: `server=` upstreams, `cache-size`, +`address=/test.local/172.44.0.2`, optional `log-queries`, and optional +`interface=en1` to scope DNS to one interface (works via netlink discovery). + +## DNSSEC + +On by default (`CONFIG_LIBDNSMASQ_DNSSEC=y`): the config ships the IANA root +trust anchor plus `dnssec` and `dnssec-check-unsigned`. Crypto comes from +Nettle/libhogweed built with bundled mini-gmp (see the lib-nettle port). To +build without it, set `CONFIG_LIBDNSMASQ_DNSSEC=n` and drop the `dnssec` lines +from the config. Note: validation compares signature timestamps against the VM +clock — if every query SERVFAILs, suspect the clock first. + +## Port notes + + +- **Patches** (in lib-dnsmasq): 0001–0003 work around lwIP's missing + `IP_PKTINFO` (without 0003, wildcard-mode UDP queries are silently + dropped); 0004 adapts DNSSEC code to Nettle 4.0's hash API; 0005 reads + netlink replies without `MSG_PEEK` (posix-netlink ignores recvmsg flags, so + a peek would consume the message and hang the next read). +- **No DHCP/TFTP server**: both need `PF_PACKET` raw sockets to talk L2 to clients without an IP; + + diff --git a/dnsmasq/rootfs/etc/dnsmasq.conf b/dnsmasq/rootfs/etc/dnsmasq.conf new file mode 100644 index 00000000..97ad976c --- /dev/null +++ b/dnsmasq/rootfs/etc/dnsmasq.conf @@ -0,0 +1,50 @@ + +no-daemon +log-facility=- + +# Resolver behaviour +no-resolv # ignore /etc/resolv.conf; use server= below +no-poll +domain-needed # don't forward names without a dot +bogus-priv # don't forward reverse lookups for private ranges +no-hosts # only use address= records below + + +listen-address=0.0.0.0 +port=53 +bind-interfaces +# Required: without it dnsmasq tries to drop privileges to "nobody", and the +# unikernel's getpwnam() only knows "root" (no /etc/passwd) -> fatal error. +user=root + +# Interface scoping works : uncomment to serve DNS +# only on the virtio (lwip names it "en1"; loopback is "lo0"). +#interface=en1 + +# cache / throughput +cache-size=10000 +min-cache-ttl=60 +dns-forward-max=1000 + +# Upstream resolvers (tried in order; add `all-servers` to race them) +server=8.8.8.8 +server=8.8.4.4 +server=1.1.1.1 + +# DNSSEC validation +# Root KSK trust anchors are baked into the image at pack time from the build +# host's dns-root-data (see .scripts/run/qemu.x86_64), so they track the host's +# package updates. dnssec-check-unsigned proves that unsigned zones are +# legitimately unsigned rather than stripped. +dnssec +conf-file=/etc/trust-anchors.conf +dnssec-check-unsigned + +# observability — logging is OFF. Uncomment to log every query +# dnsmasq still prints a few one-time +# startup lines (version, cache size, "DNSSEC validation enabled") . +#log-queries +#log-async=25 + +# Local static names (.local is mDNS-reserved; prefer .internal/.lan in real use) +address=/test.local/172.44.0.2 diff --git a/dnsmasq/setup.sh b/dnsmasq/setup.sh new file mode 100755 index 00000000..04ee0281 --- /dev/null +++ b/dnsmasq/setup.sh @@ -0,0 +1,37 @@ +#!/bin/sh + +check_exists_and_create_symlink() +{ + path="$1" + + if ! test -d workdir/"$path"; then + if ! test -d ../repos/"$path"; then + echo "No directory ../repos/$path. Run the top-level setup.sh script first." 1>&2 + exit 1 + fi + depth=$(echo "$path" | awk -F / '{ print NF }') + if test "$depth" -eq 1; then + ln -sfn ../../repos/"$path" workdir/"$path" + elif test "$depth" -eq 2; then + ln -sfn ../../../repos/"$path" workdir/"$path" + else + echo "Unknown depth of path $path." 1>&2 + exit 1 + fi + fi +} + +if ! test -d workdir; then + mkdir workdir +fi + +if ! test -d workdir/libs; then + mkdir workdir/libs +fi + + +check_exists_and_create_symlink "unikraft" +check_exists_and_create_symlink "libs/musl" +check_exists_and_create_symlink "libs/lwip" +check_exists_and_create_symlink "libs/nettle" +check_exists_and_create_symlink "libs/dnsmasq" diff --git a/dnsmasq/test.sh b/dnsmasq/test.sh new file mode 100755 index 00000000..9efebd19 --- /dev/null +++ b/dnsmasq/test.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# DNS checks against a running dnsmasq unikernel. Used both manually (after +# ./.scripts/run/qemu.x86_64) and by the CI harness (.scripts/test/wrapper.sh). +# Exits non-zero on the first failed check. Override target with HOST/PORT. +set -e + +HOST="${HOST:-127.0.0.1}" +PORT="${PORT:-5355}" + +q() { dig +short "$@" +timeout=2 +tries=1 @"$HOST" -p "$PORT"; } + +echo "UDP: test.local -> 172.44.0.2" +q test.local | grep -qx 172.44.0.2 + +echo "TCP: test.local -> 172.44.0.2" +q +tcp test.local | grep -qx 172.44.0.2 + +echo "Forwarding: unikraft.org resolves via upstream" +q unikraft.org | grep -qE '^[0-9]' + +echo "DNSSEC: cloudflare.com is validated ('ad' flag set)" +dig +timeout=3 +tries=1 @"$HOST" -p "$PORT" +dnssec cloudflare.com \ + | grep -E '^;; flags:' | grep -q ' ad' + +echo "DNSSEC: dnssec-failed.org is rejected (SERVFAIL)" +dig +timeout=3 +tries=1 @"$HOST" -p "$PORT" dnssec-failed.org \ + | grep -q 'status: SERVFAIL' + +echo "all checks passed" diff --git a/setup.sh b/setup.sh index 451b924d..7df39fbc 100755 --- a/setup.sh +++ b/setup.sh @@ -15,3 +15,8 @@ test -d repos/apps/elfloader || git clone https://github.com/unikraft/app-elfloa test -d repos/libs/redis || git clone https://github.com/unikraft/lib-redis repos/libs/redis test -d repos/libs/wamr || git clone https://github.com/unikraft/lib-wamr repos/libs/wamr test -d repos/libs/sqlite || git clone https://github.com/unikraft/lib-sqlite repos/libs/sqlite + +# dnsmasq deps. Pinned to feature-branch forks until upstreamed + +test -d repos/libs/nettle || git clone -b musl-hogweed-dnssec https://github.com/vTusharr/lib-nettle repos/libs/nettle +test -d repos/libs/dnsmasq || git clone -b dnsmasq-port https://github.com/vTusharr/lib-dnsmasq repos/libs/dnsmasq