#!/bin/sh
set -eux

cleanup() {
    echo ""
    if [ "${FAIL}" = "1" ]; then
        echo "Test failed"
        exit 1
    fi

    echo "Test passed"
    exit 0
}

FAIL=1
trap cleanup EXIT HUP INT TERM

# Install test dependencies
apt-get remove --purge cloud-init --yes
apt-get install --yes curl zfsutils-linux

# Install Incus
curl -sL https://pkgs.zabbly.com/get/incus-daily | sh

# Configure Incus
incus storage create default zfs
incus network create incusbr0 \
    ipv4.address=192.0.2.1/24 \
    ipv6.address=2001:db8::1/64 \
    ipv4.dhcp.ranges=192.0.2.2-192.0.2.199
incus profile device add default root disk path=/ pool=default

echo "=> Setting up firewall tooling and checking versions"
apt-get install --no-install-recommends --yes nftables iptables ebtables
update-alternatives --set iptables /usr/sbin/iptables-legacy
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
update-alternatives --set ebtables /usr/sbin/ebtables-legacy

# Check legacy versions installed.
iptables -v 2>&1 | grep legacy
ip6tables -v 2>&1 | grep legacy
ebtables --version 2>&1 | grep legacy

# Setup bridge filter and unmanaged bridge.s
modprobe br_netfilter
ip link add incbr0unmanaged type bridge

firewallTests() {
    incus launch images:ubuntu/focal c1
    sleep 10

    managed=0

    if incus config show c1 --expanded | grep -qF "network: incusbr0"; then
        echo "=> Performing basic DHCP/SLAAC ping tests"
        incus exec c1 -- ping -c1 192.0.2.1
        incus exec c1 -- ping -c1 2001:db8::1
        managed=1
    fi

    # Disable DHCP client and SLAAC acceptance so we don't get automatic IPs added.
    incus exec c1 -- rm -fr /etc/netplan
    incus exec c1 -- netplan apply
    incus exec c1 -- sysctl net.ipv6.conf.eth0.accept_ra=0

    echo "=> Performing faked source IP ping tests without filtering"
    incus exec c1 -- ip a flush dev eth0
    incus exec c1 -- ip a add 192.0.2.254/24 dev eth0
    incus exec c1 -- ip a add 2001:db8::254/64 dev eth0 nodad
    incus exec c1 -- ip a
    incus exec c1 -- ping -c1 192.0.2.1
    incus exec c1 -- ping -c1 2001:db8::1

    echo "=> Performing faked source IP ping tests with filtering"
    if [ $managed -eq 1 ]; then
        incus config device override c1 eth0 \
            security.mac_filtering=true \
            security.ipv4_filtering=true \
            security.ipv6_filtering=true
    else
        incus config device override c1 eth0 \
            security.mac_filtering=true \
            security.ipv4_filtering=true \
            security.ipv6_filtering=true \
            ipv4.address=192.0.2.2 \
            ipv6.address=2001:db8::2
    fi

    incus exec c1 -- ip a flush dev eth0
    incus exec c1 -- ip a add 192.0.2.254/24 dev eth0
    incus exec c1 -- ip a add 2001:db8::254/64 dev eth0 nodad
    incus exec c1 -- ip a
    ! incus exec c1 -- ping -c1 192.0.2.1 || false
    ! incus exec c1 -- ping -c1 2001:db8::1 || false

    echo "=> Performing faked source MAC ping tests without filtering"
    incus stop -f c1

    if [ $managed -eq 1 ]; then
        incus config device set c1 eth0 \
            security.mac_filtering=false \
            security.ipv4_filtering=false \
            security.ipv6_filtering=false
    else
        incus config device set c1 eth0 \
            security.mac_filtering=false \
            security.ipv4_filtering=false \
            security.ipv6_filtering=false \
            ipv4.address= \
            ipv6.address=
    fi

    incus start c1
    sleep 10
    incus exec c1 -- sysctl net.ipv6.conf.eth0.accept_ra=0
    incus exec c1 -- ip a flush dev eth0
    incus exec c1 -- ip link set dev eth0 address 00:11:22:33:44:56 up
    incus exec c1 -- ip a add 192.0.2.254/24 dev eth0
    incus exec c1 -- ip a add 2001:db8::254/64 dev eth0 nodad
    incus exec c1 -- ip a
    incus exec c1 -- ping -c1 192.0.2.1
    incus exec c1 -- ping -c1 2001:db8::1

    echo "=> Performing faked source MAC ping tests with filtering"
    incus config device set c1 eth0 security.mac_filtering=true
    incus exec c1 -- ip a
    ! incus exec c1 -- ping -c1 192.0.2.1 || false
    ! incus exec c1 -- ping -c1 2001:db8::1 || false

    incus delete -f c1
}

echo "=> Performing nftables managed bridge tests"
# Check by default on fresh system we detect and use nftables.
incus info | grep 'firewall: nftables'
incus profile device add default eth0 nic network=incusbr0
firewallTests

echo "=> Performing nftables unmanaged bridge tests"
ip a flush dev incusbr0 # Clear duplicate address from incusbr0.
ip link set incusbr0 down
ip a add 192.0.2.1/24 dev incbr0unmanaged
ip a add 2001:db8::1/64 dev incbr0unmanaged
ip link set incbr0unmanaged up
incus profile device remove default eth0
incus profile device add default eth0 nic \
    nictype=bridged \
    parent=incbr0unmanaged
firewallTests
incus profile device remove default eth0
ip a flush dev incbr0unmanaged

echo "=> Performing xtables managed bridge tests"
incus profile device add default eth0 nic network=incusbr0
nft flush ruleset
iptables -A INPUT
systemctl restart incus
sleep 5

# Check if xtables is in use we detect and use xtables.
incus info | grep 'firewall: xtables'
firewallTests

echo "=> Performing xtables unmanaged bridge tests"
ip a flush dev incusbr0 # Clear duplicate address from incusbr0.
ip link set incusbr0 down
ip a add 192.0.2.1/24 dev incbr0unmanaged
ip a add 2001:db8::1/64 dev incbr0unmanaged
ip link set incbr0unmanaged up
incus profile device remove default eth0
incus profile device add default eth0 nic \
    nictype=bridged \
    parent=incbr0unmanaged
firewallTests
incus profile device remove default eth0
ip link delete incbr0unmanaged

# Cleanup.
incus profile device remove default root
incus network delete incusbr0
incus storage delete default

# Clear firewall.
incus admin shutdown
iptables -D INPUT

FAIL=0
