#!/bin/bash
# Reproducer for syzbot WARNING in veth_napi_del_range
# https://ci.syzbot.org/series/ee732006-8545-4abd-a105-b4b1592a7baf
#
# Bug: veth_napi_del_range() iterates dev->real_num_rx_queues
# but indexes peer->txq[i], so if dev->rx_queues > peer->tx_queues
# we get: WARNING: netdev_get_tx_queue: index >= dev->num_tx_queues
#
# Trigger: create veth with asymmetric queues (rx=2, peer_tx=1),
# attach XDP, then detach it to hit veth_napi_del_range.

set -e

# Requires CONFIG_DEBUG_NET=y for the WARN_ON_ONCE in netdev_get_tx_queue()
if [ -f /proc/config.gz ]; then
    if ! zcat /proc/config.gz | grep -q "CONFIG_DEBUG_NET=y"; then
        echo "SKIP: CONFIG_DEBUG_NET=y required to trigger the WARNING"
        exit 4
    fi
fi

cleanup() {
    ip link del veth0 2>/dev/null || true
    rm -f /tmp/xdp_pass.o
}
trap cleanup EXIT

# Minimal XDP program (XDP_PASS)
# If bpftool is available, generate it; otherwise use ip link xdp obj
cat > /tmp/xdp_pass.c << 'EOF'
#define SEC(name) __attribute__((section(name), used))
#define XDP_PASS 2
struct xdp_md;
SEC("xdp")
int xdp_pass(struct xdp_md *ctx) { return XDP_PASS; }
char _license[] SEC("license") = "GPL";
EOF

# Try to compile if clang is available
if command -v clang &>/dev/null; then
    clang -O2 -g -target bpf -c /tmp/xdp_pass.c -o /tmp/xdp_pass.o
else
    echo "SKIP: clang not available to compile XDP program"
    exit 4
fi

echo "=== Creating veth pair with asymmetric queues ==="
echo "    veth0: numtxqueues=1 numrxqueues=2"
echo "    veth1: numtxqueues=1 numrxqueues=1"

# veth0 has 2 RX queues, peer (veth1) has only 1 TX queue
ip link add veth0 numtxqueues 1 numrxqueues 2 \
   type veth peer name veth1 numtxqueues 1 numrxqueues 1

ip link set veth0 up
ip link set veth1 up

echo "=== Attaching XDP to veth0 (rx=2, peer_tx=1) ==="
# This passes the XDP attach check (dev->rx >= peer->tx: 2 >= 1)
ip link set dev veth0 xdp obj /tmp/xdp_pass.o sec xdp

echo "=== Verifying queue asymmetry ==="
V0_RX=$(cat /sys/class/net/veth0/queues/rx-*/rps_cpus | wc -l)
V1_TX=$(ls -d /sys/class/net/veth1/queues/tx-* 2>/dev/null | wc -l)
echo "    veth0 rx_queues=$V0_RX  veth1 tx_queues=$V1_TX"
if [ "$V0_RX" -gt "$V1_TX" ]; then
    echo "    BUG CONDITION: dev->rx_queues ($V0_RX) > peer->tx_queues ($V1_TX)"
else
    echo "    SKIP: queue asymmetry not achieved"
    exit 4
fi

dmesg -C 2>/dev/null || true

echo "=== Detaching XDP from veth0 ==="
# This triggers veth_disable_xdp -> veth_napi_del ->
# veth_napi_del_range(veth0, 0, 2) which loops i=0,1
# and accesses peer(veth1)->txq[1] -- but peer only has 1 txq!
ip link set dev veth0 xdp off

# Check dmesg for WARNING (requires CONFIG_DEBUG_NET=y for the WARN)
if dmesg 2>/dev/null | grep -qi "WARNING.*veth_napi_del_range\|WARNING.*netdevice.h"; then
    echo "=== FAIL: WARNING in veth_napi_del_range (bug reproduced) ==="
    dmesg | grep -B1 -A10 "WARNING.*veth"
    exit 1
else
    echo "=== PASS: no WARNING in dmesg ==="
    exit 0
fi
