Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions .github/workflows/app-dnsmasq.yml
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions dnsmasq/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/workdir/
/rootfs/etc/trust-anchors.conf
/rootfs/etc/dnsmasq.conf.bak
*.cpio
*.log
*.pid
.config
.config.old
9 changes: 9 additions & 0 deletions dnsmasq/.scripts/build/qemu.arm64
Original file line number Diff line number Diff line change
@@ -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)
9 changes: 9 additions & 0 deletions dnsmasq/.scripts/build/qemu.x86_64
Original file line number Diff line number Diff line change
@@ -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)
20 changes: 20 additions & 0 deletions dnsmasq/.scripts/defconfig/qemu.arm64
Original file line number Diff line number Diff line change
@@ -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"
17 changes: 17 additions & 0 deletions dnsmasq/.scripts/defconfig/qemu.x86_64
Original file line number Diff line number Diff line change
@@ -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"
45 changes: 45 additions & 0 deletions dnsmasq/.scripts/run/qemu.arm64
Original file line number Diff line number Diff line change
@@ -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"
43 changes: 43 additions & 0 deletions dnsmasq/.scripts/run/qemu.x86_64
Original file line number Diff line number Diff line change
@@ -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"
1 change: 1 addition & 0 deletions dnsmasq/.scripts/test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/log/
48 changes: 48 additions & 0 deletions dnsmasq/.scripts/test/all.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/bin/sh
# Build and test the dnsmasq unikernel.
# Usage: all.sh [<target>] 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
57 changes: 57 additions & 0 deletions dnsmasq/.scripts/test/common.sh
Original file line number Diff line number Diff line change
@@ -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"
26 changes: 26 additions & 0 deletions dnsmasq/.scripts/test/wrapper.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash
# Start an instance, run a single round of DNS checks
# Usage: ./.scripts/test/wrapper.sh <start_command>

. ./.scripts/test/common.sh

if test $# -lt 1; then
echo "Usage: $0 <start_command>" 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
26 changes: 26 additions & 0 deletions dnsmasq/Config.uk
Original file line number Diff line number Diff line change
@@ -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
Loading