Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .config.ci
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ CONFIG_PACKAGE_matter-netman-openssl=m
CONFIG_PACKAGE_mdnsresponder=m
CONFIG_PACKAGE_openthread-br=m
CONFIG_PACKAGE_openthread-rcp-nrf52840-mdk=m
CONFIG_PACKAGE_wpad-mbedtls=m
CONFIG_PACKAGE_wpad-openssl=m
CONFIG_PACKAGE_wpad-wolfssl=m
55 changes: 55 additions & 0 deletions .containers/hostapd-hwsim-runner/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
FROM --platform=linux/amd64 debian:trixie-slim

# The tests need mac80211_hwsim, which is not available for the kernel of a
# GitHub Actions runner, so qemu-run executes them in a nested VM built around
# the generic Debian kernel.
ENV DEBIAN_FRONTEND=noninteractive
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
# hostap build
build-essential git pkg-config \
autoconf automake libtool \
binutils-dev libiberty-dev \
libnl-3-dev libnl-genl-3-dev libnl-route-3-dev \
libmbedtls-dev libssl-dev \
libdbus-1-dev libsqlite3-dev libpcap-dev libxml2-dev libcurl4-openssl-dev \
# test runtime
python3 python3-pip \
iproute2 iw bridge-utils net-tools \
kmod procps psmisc openssl wireless-regdb \
faketime ca-certificates \
qemu-system-x86 virtiofsd; \
# Download and unpack the kernel image directly; this avoids pulling in
# it's runtime dependencies (linux-base, systemd, udev, ...) and also
# avoids running any post-install hooks like initramfs generation.
kpkg="$(apt-cache depends linux-image-amd64 | grep Depends | grep -o 'linux-image-.*')"; \
cd /var/cache; apt-get download "$kpkg"; dpkg-deb -x linux-image-*.deb /; \
kver="$(ls /lib/modules)"; depmod "$kver"; \
cd /boot; ln -s "vmlinuz-$kver" vmlinuz-qemu; \
# Clean up
rm -rf /var/cache/linux-image-*.deb /var/lib/apt/lists/*

COPY qemu-run /usr/local/bin/qemu-run

# The Debian wolfssl package does not enable all the options hostapd requires.
# Build from source enabling everything hostapd's crypto_wolfssl.c / DPP / EAP
# code references: --enable-all is the feature umbrella (PKCS7, cert gen/req,
# HPKE, ...) and --enable-wpas adds the wpa_supplicant-specific bits that
# --enable-all does not (notably HAVE_SECRET_CALLBACK, HAVE_KEYING_MATERIAL).
RUN cd /usr/local/src \
&& git clone --depth 1 --single-branch --branch v5.9.2-stable https://github.com/wolfSSL/wolfssl \
&& cd wolfssl \
&& ./autogen.sh && ./configure --enable-all --enable-wpas --enable-hpke \
&& make -j"$(nproc)" && make install && ldconfig \
&& cd / && rm -rf /usr/local/src/wolfssl

# Test-framework Python deps (the packaged versions are known-problematic, so use pip;
# Debian marks the base env externally-managed, hence --break-system-packages).
RUN pip3 install --no-cache-dir --break-system-packages pycryptodome pyrad

# hwsim tests launch hostapd/wpa_supplicant etc. via sudo unconditionally,
# which strips LD_PRELOAD and breaks using faketime to work around expired
# test certificates. Use a sudo stub similar to tests/hwsim/vm/inside.sh.
RUN printf '%s\n' '#!/bin/sh' 'exec "$@"' >/usr/local/bin/sudo \
&& chmod +x /usr/local/bin/sudo
124 changes: 124 additions & 0 deletions .containers/hostapd-hwsim-runner/qemu-run
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#!/bin/bash
#
# Run a command inside a qemu VM that shares the host's file system.
#
# The arguments are a command and its arguments, not a shell command string;
# word boundaries are preserved as they would be by sudo or docker run, rather
# than being re-split by a shell as they are by ssh.
# If no arguments are given, a shell script is read from stdin instead. This
# convenient when shell syntax is required, especially combined with << or <<<,
# e.g. qemu-run <<<'test -e /dev/shm && echo yes'
# The exit status of the command is propagated as the exit status of qemu-run.

set -eu

VM_MEMORY="${VM_MEMORY:-2048}"
VM_CPUS="${VM_CPUS:-$(nproc)}"

# Deliberately not under /tmp, which the tests use for their own purposes.
JOB="$(mktemp -d -p /var/tmp)"

# virtiofsd normally exits by itself once qemu disconnects, so the kill only
# matters when we bail out before or during the qemu run. It has to tolerate the
# process already being gone: under set -e a failure here would both skip the
# rest of the cleanup and override the exit status we are trying to propagate.
cleanup() {
[[ -z "${VFSD:-}" ]] || kill "$VFSD" 2>/dev/null || true
rm -rf -- "$JOB"
}
trap cleanup EXIT

# Assemble the VM's init script
{
# /run has to be a local file system, because the hostapd and wpa_supplicant
# control interfaces create unix sockets there. /dev has to be devtmpfs
# rather than the container's shared /dev, because mac80211_hwsim needs its
# own rfkill device nodes -- which in turn means /dev/shm and /dev/pts have
# to be mounted by hand, as devtmpfs provides neither. faketime needs the
# former: it passes the clock offset to child processes through a POSIX
# shared memory object.
printf '%s\n' \
'#!/bin/bash' \
'mount -t devtmpfs devtmpfs /dev' \
'exec </dev/console >/dev/console 2>&1' \
'mkdir -p /dev/shm /dev/pts' \
'mount -t tmpfs -o mode=1777,nosuid,nodev tmpfs /dev/shm' \
'mount -t devpts -o gid=5,mode=620,ptmxmode=666 devpts /dev/pts' \
'mount -t proc proc /proc' \
'mount -t sysfs sysfs /sys' \
'mount -t debugfs debugfs /sys/kernel/debug' \
'mount -t tmpfs tmpfs /run' \
'ip link set lo up # some tests talk to 127.0.0.1' \
'export USER=root LOGNAME=root HOME=/root' \
'export debian_chroot=qemu' \
'echo 1 >/proc/sys/kernel/sysrq'
# Propagate a few values from the host
printf 'hostname %q\n' "$(hostname)"
printf 'export TERM=%q\n' "${TERM:-dumb}" # for an interactive shell
printf 'cd %q\n' "$PWD"
# Delimit the command's output from the kernel's boot/shutdown chatter.
printf 'echo === >&2\n'
# Transmit the exit status, then stop the VM using Sysrq. The sleep keeps
# init alive until it takes effect, since the kernel panics if pid 1 exits.
printf 'trap %q EXIT\n' \
'rc=$?; echo "$rc" >'"$JOB"'/rc; echo === >&2; echo o >/proc/sysrq-trigger; sleep 60'
# Emit the job body, in a subshell so that an exec in the job replaces that
# rather than init, which would discard the trap above. Bash resets the trap
# to its default in the subshell, so it stays owned by init alone. The no-op
# keeps the subshell valid even if the job turns out to be empty.
printf '( :\n'
if (( $# )); then
printf ' %q' "$@"
else
cat
fi
printf '\n)\n'
} >"$JOB/init"
chmod 0755 "$JOB/init"

# --sandbox=none because the default sandbox needs privileges the container does
# not have, and would hide any volumes mounted into it. --announce-submounts
# keeps inode numbers distinct across the several mounts that / spans.
/usr/libexec/virtiofsd \
--socket-path="$JOB/vfs.sock" --shared-dir=/ \
--sandbox=none --seccomp=none --rlimit-nofile 0 \
--announce-submounts --cache=auto \
--log-level warn &
VFSD=$!
while kill -0 "$VFSD" 2>/dev/null && [[ ! -e "$JOB/vfs.sock" ]]; do sleep 0.05; done
if [[ ! -e "$JOB/vfs.sock" ]]; then
echo "qemu-run: virtiofsd failed to start" >&2
exit 1
fi

# KVM is available on GitHub-hosted runners, but fall back to emulation so that
# this also works elsewhere (much slower, and the tests are timing sensitive).
if [[ -w /dev/kvm ]]; then
ACCEL=kvm CPU=host
else
echo "qemu-run: /dev/kvm not available, falling back to emulation" >&2
ACCEL=tcg CPU=max
fi

# memory-backend=mem gives virtiofsd a shareable view of the guest's memory, as
# vhost-user requires. No initrd is needed because the Debian kernel has
# fuse/virtiofs and the virtio transports built in, and can mount a non-block
# root filesystem by itself. -net none and -vga none drop devices the VM has no
# use for, including the network card whose option ROM the BIOS would otherwise
# probe for a boot image; sercon-port stops SeaBIOS writing its banner (and a
# terminal reset) to the serial console, which it does because there is no VGA.
qemu-system-x86_64 \
-machine "accel=$ACCEL,memory-backend=mem" -cpu "$CPU" \
-smp "$VM_CPUS" -m "$VM_MEMORY" \
-nographic -monitor none -no-reboot -net none -vga none \
-fw_cfg name=etc/sercon-port,string=0 \
-object memory-backend-memfd,id=mem,size="${VM_MEMORY}M",share=on \
-chardev socket,id=vfs,path="$JOB/vfs.sock" \
-device vhost-user-fs-pci,queue-size=1024,chardev=vfs,tag=rootfs \
-object rng-random,filename=/dev/urandom,id=rng0 \
-device virtio-rng-pci,rng=rng0 \
-kernel /boot/vmlinuz-qemu \
-append "quiet console=ttyS0 root=rootfs rootfstype=virtiofs rw mitigations=off panic=-1 init=$JOB/init"

read -r 2>/dev/null <"$JOB/rc" || echo "qemu-run: failed to read job exit status" >&2
exit "${REPLY:-127}"
20 changes: 20 additions & 0 deletions .github/actions/openwrt-version/action.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: openwrt version
description: Determines OPENWRT_VERSION from an override input or the Dockerfile

inputs:
openwrt-version-override:
description: OpenWrt version number override
required: false
default: ""

runs:
using: composite
steps:
- name: Determine the OpenWrt version
shell: bash
run: |
if [[ -n "${{ inputs.openwrt-version-override }}" ]]; then
echo "OPENWRT_VERSION=${{ inputs.openwrt-version-override }}"
else
grep '^ARG OPENWRT_VERSION=' .containers/matter-openwrt-build/Dockerfile | cut -c 5-
fi | tee -a "$GITHUB_ENV"
36 changes: 36 additions & 0 deletions .github/workflows/build-hostapd-hwsim-runner.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: build hostapd-hwsim-runner container

on:
pull_request:
paths:
- .containers/hostapd-hwsim-runner/**
- .github/workflows/build-hostapd-hwsim-runner.yaml
push:
paths:
- .containers/hostapd-hwsim-runner/**
- .github/workflows/build-hostapd-hwsim-runner.yaml
workflow_dispatch:

permissions:
packages: write

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v6

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Build and push
uses: elgohr/Publish-Docker-Github-Action@v5
with:
name: project-chip/hostapd-hwsim-runner
context: .containers/hostapd-hwsim-runner
registry: ghcr.io
tags: "latest,${{ github.sha }}"
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
no_push: ${{ github.repository_owner != 'project-chip' || github.event_name == 'pull_request' || github.event.ref != 'refs/heads/main' }}
14 changes: 4 additions & 10 deletions .github/workflows/build-matter-openwrt-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,10 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Prepare meta-data
id: prepare
shell: bash
run: |
# Determine OPENWRT_VERSION from override input or Dockerfile
if [[ -n "${{ inputs.openwrt-version-override }}" ]]; then
echo "OPENWRT_VERSION=${{ inputs.openwrt-version-override }}"
else
grep '^ARG OPENWRT_VERSION=' .containers/matter-openwrt-build/Dockerfile | cut -c 5-
fi | tee -a "$GITHUB_ENV"
- name: Determine OpenWrt version
uses: ./.github/actions/openwrt-version
with:
openwrt-version-override: ${{ inputs.openwrt-version-override }}

- name: Build and push
uses: elgohr/Publish-Docker-Github-Action@v5
Expand Down
14 changes: 12 additions & 2 deletions .github/workflows/build-packages.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ name: build matter packages
on:
pull_request:
push:
workflow_dispatch:
inputs:
openwrt-version-override:
type: string
description: OpenWrt version number override

jobs:
build:
Expand All @@ -11,6 +16,11 @@ jobs:
- name: Checkout
uses: actions/checkout@v6

- name: Determine OpenWrt version
uses: ./.github/actions/openwrt-version
with:
openwrt-version-override: ${{ inputs.openwrt-version-override }}

- name: Prepare log directory
run: |
mkdir -p ${{ runner.temp }}/logs
Expand All @@ -22,11 +32,11 @@ jobs:
- name: Build
uses: addnab/docker-run-action@v3
with:
image: ghcr.io/project-chip/matter-openwrt-build:25.12.5
image: ghcr.io/project-chip/matter-openwrt-build:${{ env.OPENWRT_VERSION }}
options: --volume ${{ github.workspace }}:/workspace --volume ${{ runner.temp }}/logs:/builder/logs
shell: bash
run: |
set -x
set -eux
echo "src-link --force matter /workspace" >>feeds.conf
./scripts/feeds update matter
./scripts/feeds install -a -p matter
Expand Down
Loading
Loading