Skip to content

Kernel Compatibility Test #199

Kernel Compatibility Test

Kernel Compatibility Test #199

# Credits: https://github.com/tokio-rs/io-uring/blob/master/.github/workflows/kernel-version-test.yml
#
# Tests kTLS functionality across multiple kernel versions.
# Manual trigger supports custom space-separated version list.
name: Kernel Compatibility Test
on:
push:
branches: ["main"]
paths:
- "crates/**"
- "Cargo.toml"
- "Cargo.lock"
- ".github/workflows/kernel-compatibility-test.yml"
pull_request:
paths:
- "crates/**"
- "Cargo.toml"
- "Cargo.lock"
- ".github/workflows/kernel-compatibility-test.yml"
merge_group:
paths:
- "crates/**"
- "Cargo.toml"
- "Cargo.lock"
- ".github/workflows/kernel-compatibility-test.yml"
schedule:
# Every day at 04:00 UTC
- cron: "0 4 * * *"
workflow_dispatch:
inputs:
version:
description: "The Linux kernel version to test"
required: true
source:
description: "The compressed source code's download URL"
required: true
permissions:
contents: read
jobs:
prepare-kernel-versions:
name: Prepare kernel versions matrix
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
steps:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y jq curl
- name: Set matrix
id: set-matrix
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
json_array="[{\"version\":\"${{ github.event.inputs.version }}\",\"source\":\"${{ github.event.inputs.source }}\"}]"
else
echo "Fetching latest kernel versions from kernel.org..."
releases_json=$(curl -s https://www.kernel.org/releases.json)
if [ -z "$releases_json" ]; then
echo "Failed to fetch kernel releases data"
exit 1
fi
latest_releases=$(echo "$releases_json" | jq -c '[.releases[] | select(.source != null) | {version, source}]')
fixed_versions='[
{"version": "6.12", "source": "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.tar.xz"},
{"version": "6.6", "source": "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.tar.xz"},
{"version": "6.1.28", "source": "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.28.tar.xz"},
{"version": "5.15.25", "source": "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.25.tar.xz"},
{"version": "5.10.102", "source": "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.102.tar.xz"},
{"version": "5.4.181", "source": "https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.181.tar.xz"}
]'
json_array=$(echo "$latest_releases $fixed_versions" | jq -c -s '.[0] + .[1] | unique_by(.version) | sort_by(.version | gsub("-.*"; "") | split(".") | map(tonumber)) | reverse')
fi
echo "matrix=$json_array" >> $GITHUB_OUTPUT
echo "Generated matrix: $json_array"
build-test-binary:
name: Build test binary
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y musl-tools
- name: Install Rust 1.83.0
uses: dtolnay/rust-toolchain@master
with:
toolchain: 1.83.0
targets: x86_64-unknown-linux-musl
- name: Build test binary
run: |
cargo build --package ktls-tests --bin ktls-test-echo --release --target x86_64-unknown-linux-musl
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ktls-test-echo
path: target/x86_64-unknown-linux-musl/release/ktls-test-echo
retention-days: 1
compression-level: 0
overwrite: true
build-kernel:
name: Build kernel - ${{ matrix.version }}
needs: prepare-kernel-versions
runs-on: ubuntu-latest
strategy:
matrix:
include: ${{fromJson(needs.prepare-kernel-versions.outputs.matrix)}}
fail-fast: false
env:
KERNEL_VERSION: ${{ matrix.version }}
KERNEL_SOURCE_URL: ${{ matrix.source }}
steps:
- name: Cache Linux source
id: cache-kernel
uses: actions/cache@v4
with:
path: linux-${{ env.KERNEL_VERSION }}
key: linux-${{ env.KERNEL_VERSION }}-cache-v1
- name: Install dependencies
if: steps.cache-kernel.outputs.cache-hit != 'true'
run: |
sudo apt-get update
sudo apt-get install -y \
bison flex libelf-dev \
xz-utils wget
- name: Build Linux kernel
if: steps.cache-kernel.outputs.cache-hit != 'true'
run: |
# Download kernel source from the provided URL
filename=$(basename "$KERNEL_SOURCE_URL")
wget "$KERNEL_SOURCE_URL" -O "$filename"
# Extract based on file extension
if [[ "$filename" == *.tar.xz ]]; then
tar xf "$filename"
elif [[ "$filename" == *.tar.gz ]]; then
tar xzf "$filename"
else
echo "Unsupported archive format: $filename"
exit 1
fi
cd linux-${KERNEL_VERSION}
# Generate the default config
make defconfig
# Enable essentials as built-ins
scripts/config --enable CONFIG_DEVTMPFS
scripts/config --enable CONFIG_DEVTMPFS_MOUNT
# Enable virtio drivers
scripts/config --enable CONFIG_VIRTIO
scripts/config --enable CONFIG_VIRTIO_PCI
scripts/config --enable CONFIG_VIRTIO_BLK
# Enable kTLS support
scripts/config --enable CONFIG_TLS
scripts/config --enable CONFIG_TLS_DEVICE
# Generate the updated config
make olddefconfig
make -j$(nproc)
- name: Upload kernel artifact
uses: actions/upload-artifact@v4
with:
name: linux-kernel-${{ env.KERNEL_VERSION }}
path: linux-${{ env.KERNEL_VERSION }}/arch/x86/boot/bzImage
retention-days: 1
compression-level: 6
overwrite: true
test:
name: Test - ${{ matrix.kernel.version }} @ ${{ matrix.arg-termination }} ${{ matrix.arg-cipher }}
needs: [prepare-kernel-versions, build-test-binary, build-kernel]
runs-on: ubuntu-latest
strategy:
matrix:
kernel: ${{fromJson(needs.prepare-kernel-versions.outputs.matrix)}}
arg-termination: ["client", "server"]
arg-cipher:
[
"TLS13_AES_128_GCM_SHA256",
"TLS13_AES_256_GCM_SHA384",
"TLS13_CHACHA20_POLY1305_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256",
]
fail-fast: false
env:
KERNEL_VERSION: ${{ matrix.kernel.version }}
TEST_ARG_TERMINATION: ${{ matrix.arg-termination }}
TEST_ARG_CIPHER: ${{ matrix.arg-cipher }}
steps:
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y \
qemu-system-x86 busybox-static cpio e2fsprogs
- name: Download test binary artifact
uses: actions/download-artifact@v4
with:
name: ktls-test-echo
path: .
- name: Download kernel artifact
uses: actions/download-artifact@v4
with:
name: linux-kernel-${{ env.KERNEL_VERSION }}
path: .
- name: Prepare initramfs + tests binaries
run: |
rm -rf initramfs && mkdir -p initramfs/{bin,sbin,proc,sys,tmp}
# Copy the test binary (downloaded from artifact)
chmod +x ktls-test-echo
cp ktls-test-echo initramfs/bin/
# Add necessary binaries from busybox
cp /usr/bin/busybox initramfs/bin/
for cmd in sh mount ip ifconfig cat; do ln -sf busybox initramfs/bin/$cmd; done
ln -sf ../bin/busybox initramfs/sbin/poweroff
# Generate init script
cat > initramfs/init << EOF
#!/bin/sh
set -e
# Activating the loopback interface (it's required for some network tests)
ip link set lo up
mkdir -p /dev
# Enable necessary devices
# https://www.kernel.org/doc/Documentation/admin-guide/devices.txt
mknod /dev/port c 1 4
mknod /dev/null c 1 3
mknod /dev/zero c 1 5
mknod /dev/tty c 5 0
mkdir -p /tmp && mount -t tmpfs -o mode=1777 tmpfs /tmp
# Bring up ext4 test volume at /mnt
mount -t devtmpfs devtmpfs /dev
exit_code=0
# Run the test binary
RUST_BACKTRACE=1 /bin/ktls-test-echo -t "${TEST_ARG_TERMINATION}" -c "${TEST_ARG_CIPHER}" || exit_code=1
# If the test binary exited with a non-zero code, write it to /dev/port.
# This lets QEMU exit with non-zero exit-code, triggering a CI error.
[ \$exit_code -eq 0 ] || printf '\x01' \\
| dd of=/dev/port bs=1 seek=244 count=1 2>/dev/null
/sbin/poweroff -f
EOF
chmod +x initramfs/init
# Pack into a CPIO archive
(cd initramfs && find . -print0 \
| cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz)
- name: Run tests in QEMU
run: |
qemu-system-x86_64 \
-device isa-debug-exit,iobase=0xf4,iosize=0x04 \
-kernel bzImage \
-initrd initramfs.cpio.gz \
-netdev user,id=net0 \
-device e1000,netdev=net0 \
-append "console=ttyS0 rootfstype=ramfs panic=1" \
-nographic -no-reboot -m 1024 -action panic=exit-failure
if [ $? -ne 0 ]; then
echo "tests failed (QEMU exited abnormally)"
exit 1
else
echo "all tests passed"
fi