#!/bin/bash # SPDX-License-Identifier: GPL-2.0 MY_DIR=$(dirname $0) # Details on the bpf prog BPF_CGRP2_ARRAY_NAME='test_cgrp2_array_pin' BPF_PROG="$MY_DIR/test_cgrp2_tc_kern.o" BPF_SECTION='filter' [ -z "$TC" ] && TC='tc' [ -z "$IP" ] && IP='ip' # Names of the veth interface, net namespace...etc. HOST_IFC='ve' NS_IFC='vens' NS='ns' find_mnt() { cat /proc/mounts | \ awk '{ if ($3 == "'$1'" && mnt == "") { mnt = $2 }} END { print mnt }' } # Init cgroup2 vars init_cgrp2_vars() { CGRP2_ROOT=$(find_mnt cgroup2) if [ -z "$CGRP2_ROOT" ] then CGRP2_ROOT='/mnt/cgroup2' MOUNT_CGRP2="yes" fi CGRP2_TC="$CGRP2_ROOT/tc" CGRP2_TC_LEAF="$CGRP2_TC/leaf" } # Init bpf fs vars init_bpf_fs_vars() { local bpf_fs_root=$(find_mnt bpf) [ -n "$bpf_fs_root" ] || return -1 BPF_FS_TC_SHARE="$bpf_fs_root/tc/globals" } setup_cgrp2() { case $1 in start) if [ "$MOUNT_CGRP2" == 'yes' ] then [ -d $CGRP2_ROOT ] || mkdir -p $CGRP2_ROOT mount -t cgroup2 none $CGRP2_ROOT || return $? fi mkdir -p $CGRP2_TC_LEAF ;; *) rmdir $CGRP2_TC_LEAF && rmdir $CGRP2_TC [ "$MOUNT_CGRP2" == 'yes' ] && umount $CGRP2_ROOT ;; esac } setup_bpf_cgrp2_array() { local bpf_cgrp2_array="$BPF_FS_TC_SHARE/$BPF_CGRP2_ARRAY_NAME" case $1 in start) $MY_DIR/test_cgrp2_array_pin -U $bpf_cgrp2_array -v $CGRP2_TC ;; *) [ -d "$BPF_FS_TC_SHARE" ] && rm -f $bpf_cgrp2_array ;; esac } setup_net() { case $1 in start) $IP link add $HOST_IFC type veth peer name $NS_IFC || return $? $IP link set dev $HOST_IFC up || return $? sysctl -q net.ipv6.conf.$HOST_IFC.accept_dad=0 $IP netns add ns || return $? $IP link set dev $NS_IFC netns ns || return $? $IP -n $NS link set dev $NS_IFC up || return $? $IP netns exec $NS sysctl -q net.ipv6.conf.$NS_IFC.accept_dad=0 $TC qdisc add dev $HOST_IFC clsact || return $? $TC filter add dev $HOST_IFC egress bpf da obj $BPF_PROG sec $BPF_SECTION || return $? ;; *) $IP netns del $NS $IP link del $HOST_IFC ;; esac } run_in_cgrp() { # Fork another bash and move it under the specified cgroup. # It makes the cgroup cleanup easier at the end of the test. cmd='echo $$ > ' cmd="$cmd $1/cgroup.procs; exec $2" bash -c "$cmd" } do_test() { run_in_cgrp $CGRP2_TC_LEAF "ping -6 -c3 ff02::1%$HOST_IFC >& /dev/null" local dropped=$($TC -s qdisc show dev $HOST_IFC | tail -3 | \ awk '/drop/{print substr($7, 0, index($7, ",")-1)}') if [[ $dropped -eq 0 ]] then echo "FAIL" return 1 else echo "Successfully filtered $dropped packets" return 0 fi } do_exit() { if [ "$DEBUG" == "yes" ] && [ "$MODE" != 'cleanuponly' ] then echo "------ DEBUG ------" echo "mount: "; mount | egrep '(cgroup2|bpf)'; echo echo "$CGRP2_TC_LEAF: "; ls -l $CGRP2_TC_LEAF; echo if [ -d "$BPF_FS_TC_SHARE" ] then echo "$BPF_FS_TC_SHARE: "; ls -l $BPF_FS_TC_SHARE; echo fi echo "Host net:" $IP netns $IP link show dev $HOST_IFC $IP -6 a show dev $HOST_IFC $TC -s qdisc show dev $HOST_IFC echo echo "$NS net:" $IP -n $NS link show dev $NS_IFC $IP -n $NS -6 link show dev $NS_IFC echo "------ DEBUG ------" echo fi if [ "$MODE" != 'nocleanup' ] then setup_net stop setup_bpf_cgrp2_array stop setup_cgrp2 stop fi } init_cgrp2_vars init_bpf_fs_vars while [[ $# -ge 1 ]] do a="$1" case $a in debug) DEBUG='yes' shift 1 ;; cleanup-only) MODE='cleanuponly' shift 1 ;; no-cleanup) MODE='nocleanup' shift 1 ;; *) echo "test_cgrp2_tc [debug] [cleanup-only | no-cleanup]" echo " debug: Print cgrp and network setup details at the end of the test" echo " cleanup-only: Try to cleanup things from last test. No test will be run" echo " no-cleanup: Run the test but don't do cleanup at the end" echo "[Note: If no arg is given, it will run the test and do cleanup at the end]" echo exit -1 ;; esac done trap do_exit 0 [ "$MODE" == 'cleanuponly' ] && exit setup_cgrp2 start || exit $? setup_net start || exit $? init_bpf_fs_vars || exit $? setup_bpf_cgrp2_array start || exit $? do_test echo