|  | #!/bin/bash | 
|  | # SPDX-License-Identifier: GPL-2.0 | 
|  |  | 
|  | # Uncomment to see generated bytecode | 
|  | #VERBOSE=verbose | 
|  |  | 
|  | NS1=lwt_ns1 | 
|  | NS2=lwt_ns2 | 
|  | VETH0=tst_lwt1a | 
|  | VETH1=tst_lwt1b | 
|  | VETH2=tst_lwt2a | 
|  | VETH3=tst_lwt2b | 
|  | IPVETH0="192.168.254.1" | 
|  | IPVETH1="192.168.254.2" | 
|  | IPVETH1b="192.168.254.3" | 
|  |  | 
|  | IPVETH2="192.168.111.1" | 
|  | IPVETH3="192.168.111.2" | 
|  |  | 
|  | IP_LOCAL="192.168.99.1" | 
|  |  | 
|  | PROG_SRC="test_lwt_bpf.c" | 
|  | BPF_PROG="test_lwt_bpf.o" | 
|  | TRACE_ROOT=/sys/kernel/tracing | 
|  | CONTEXT_INFO=$(cat ${TRACE_ROOT}/trace_options | grep context) | 
|  |  | 
|  | function lookup_mac() | 
|  | { | 
|  | set +x | 
|  | if [ ! -z "$2" ]; then | 
|  | MAC=$(ip netns exec $2 ip link show $1 | grep ether | awk '{print $2}') | 
|  | else | 
|  | MAC=$(ip link show $1 | grep ether | awk '{print $2}') | 
|  | fi | 
|  | MAC="${MAC//:/}" | 
|  | echo "0x${MAC:10:2}${MAC:8:2}${MAC:6:2}${MAC:4:2}${MAC:2:2}${MAC:0:2}" | 
|  | set -x | 
|  | } | 
|  |  | 
|  | function cleanup { | 
|  | set +ex | 
|  | rm $BPF_PROG 2> /dev/null | 
|  | ip link del $VETH0 2> /dev/null | 
|  | ip link del $VETH1 2> /dev/null | 
|  | ip link del $VETH2 2> /dev/null | 
|  | ip link del $VETH3 2> /dev/null | 
|  | ip netns exec $NS1 killall netserver | 
|  | ip netns delete $NS1 2> /dev/null | 
|  | ip netns delete $NS2 2> /dev/null | 
|  | set -ex | 
|  | } | 
|  |  | 
|  | function setup_one_veth { | 
|  | ip netns add $1 | 
|  | ip link add $2 type veth peer name $3 | 
|  | ip link set dev $2 up | 
|  | ip addr add $4/24 dev $2 | 
|  | ip link set $3 netns $1 | 
|  | ip netns exec $1 ip link set dev $3 up | 
|  | ip netns exec $1 ip addr add $5/24 dev $3 | 
|  |  | 
|  | if [ "$6" ]; then | 
|  | ip netns exec $1 ip addr add $6/32 dev $3 | 
|  | fi | 
|  | } | 
|  |  | 
|  | function get_trace { | 
|  | set +x | 
|  | cat ${TRACE_ROOT}/trace | grep -v '^#' | 
|  | set -x | 
|  | } | 
|  |  | 
|  | function cleanup_routes { | 
|  | ip route del ${IPVETH1}/32 dev $VETH0 2> /dev/null || true | 
|  | ip route del table local local ${IP_LOCAL}/32 dev lo 2> /dev/null || true | 
|  | } | 
|  |  | 
|  | function install_test { | 
|  | cleanup_routes | 
|  | cp /dev/null ${TRACE_ROOT}/trace | 
|  |  | 
|  | OPTS="encap bpf headroom 14 $1 obj $BPF_PROG section $2 $VERBOSE" | 
|  |  | 
|  | if [ "$1" == "in" ];  then | 
|  | ip route add table local local ${IP_LOCAL}/32 $OPTS dev lo | 
|  | else | 
|  | ip route add ${IPVETH1}/32 $OPTS dev $VETH0 | 
|  | fi | 
|  | } | 
|  |  | 
|  | function remove_prog { | 
|  | if [ "$1" == "in" ];  then | 
|  | ip route del table local local ${IP_LOCAL}/32 dev lo | 
|  | else | 
|  | ip route del ${IPVETH1}/32 dev $VETH0 | 
|  | fi | 
|  | } | 
|  |  | 
|  | function filter_trace { | 
|  | # Add newline to allow starting EXPECT= variables on newline | 
|  | NL=$'\n' | 
|  | echo "${NL}$*" | sed -e 's/bpf_trace_printk: //g' | 
|  | } | 
|  |  | 
|  | function expect_fail { | 
|  | set +x | 
|  | echo "FAIL:" | 
|  | echo "Expected: $1" | 
|  | echo "Got: $2" | 
|  | set -x | 
|  | exit 1 | 
|  | } | 
|  |  | 
|  | function match_trace { | 
|  | set +x | 
|  | RET=0 | 
|  | TRACE=$1 | 
|  | EXPECT=$2 | 
|  | GOT="$(filter_trace "$TRACE")" | 
|  |  | 
|  | [ "$GOT" != "$EXPECT" ] && { | 
|  | expect_fail "$EXPECT" "$GOT" | 
|  | RET=1 | 
|  | } | 
|  | set -x | 
|  | return $RET | 
|  | } | 
|  |  | 
|  | function test_start { | 
|  | set +x | 
|  | echo "----------------------------------------------------------------" | 
|  | echo "Starting test: $*" | 
|  | echo "----------------------------------------------------------------" | 
|  | set -x | 
|  | } | 
|  |  | 
|  | function failure { | 
|  | get_trace | 
|  | echo "FAIL: $*" | 
|  | exit 1 | 
|  | } | 
|  |  | 
|  | function test_ctx_xmit { | 
|  | test_start "test_ctx on lwt xmit" | 
|  | install_test xmit test_ctx | 
|  | ping -c 3 $IPVETH1 || { | 
|  | failure "test_ctx xmit: packets are dropped" | 
|  | } | 
|  | match_trace "$(get_trace)" " | 
|  | len 84 hash 0 protocol 8 | 
|  | cb 1234 ingress_ifindex 0 ifindex $DST_IFINDEX | 
|  | len 84 hash 0 protocol 8 | 
|  | cb 1234 ingress_ifindex 0 ifindex $DST_IFINDEX | 
|  | len 84 hash 0 protocol 8 | 
|  | cb 1234 ingress_ifindex 0 ifindex $DST_IFINDEX" || exit 1 | 
|  | remove_prog xmit | 
|  | } | 
|  |  | 
|  | function test_ctx_out { | 
|  | test_start "test_ctx on lwt out" | 
|  | install_test out test_ctx | 
|  | ping -c 3 $IPVETH1 || { | 
|  | failure "test_ctx out: packets are dropped" | 
|  | } | 
|  | match_trace "$(get_trace)" " | 
|  | len 84 hash 0 protocol 8 | 
|  | cb 1234 ingress_ifindex 0 ifindex 0 | 
|  | len 84 hash 0 protocol 8 | 
|  | cb 1234 ingress_ifindex 0 ifindex 0 | 
|  | len 84 hash 0 protocol 8 | 
|  | cb 1234 ingress_ifindex 0 ifindex 0" || exit 1 | 
|  | remove_prog out | 
|  | } | 
|  |  | 
|  | function test_ctx_in { | 
|  | test_start "test_ctx on lwt in" | 
|  | install_test in test_ctx | 
|  | ping -c 3 $IP_LOCAL || { | 
|  | failure "test_ctx out: packets are dropped" | 
|  | } | 
|  | # We will both request & reply packets as the packets will | 
|  | # be from $IP_LOCAL => $IP_LOCAL | 
|  | match_trace "$(get_trace)" " | 
|  | len 84 hash 0 protocol 8 | 
|  | cb 1234 ingress_ifindex 1 ifindex 1 | 
|  | len 84 hash 0 protocol 8 | 
|  | cb 1234 ingress_ifindex 1 ifindex 1 | 
|  | len 84 hash 0 protocol 8 | 
|  | cb 1234 ingress_ifindex 1 ifindex 1 | 
|  | len 84 hash 0 protocol 8 | 
|  | cb 1234 ingress_ifindex 1 ifindex 1 | 
|  | len 84 hash 0 protocol 8 | 
|  | cb 1234 ingress_ifindex 1 ifindex 1 | 
|  | len 84 hash 0 protocol 8 | 
|  | cb 1234 ingress_ifindex 1 ifindex 1" || exit 1 | 
|  | remove_prog in | 
|  | } | 
|  |  | 
|  | function test_data { | 
|  | test_start "test_data on lwt $1" | 
|  | install_test $1 test_data | 
|  | ping -c 3 $IPVETH1 || { | 
|  | failure "test_data ${1}: packets are dropped" | 
|  | } | 
|  | match_trace "$(get_trace)" " | 
|  | src: 1fea8c0 dst: 2fea8c0 | 
|  | src: 1fea8c0 dst: 2fea8c0 | 
|  | src: 1fea8c0 dst: 2fea8c0" || exit 1 | 
|  | remove_prog $1 | 
|  | } | 
|  |  | 
|  | function test_data_in { | 
|  | test_start "test_data on lwt in" | 
|  | install_test in test_data | 
|  | ping -c 3 $IP_LOCAL || { | 
|  | failure "test_data in: packets are dropped" | 
|  | } | 
|  | # We will both request & reply packets as the packets will | 
|  | # be from $IP_LOCAL => $IP_LOCAL | 
|  | match_trace "$(get_trace)" " | 
|  | src: 163a8c0 dst: 163a8c0 | 
|  | src: 163a8c0 dst: 163a8c0 | 
|  | src: 163a8c0 dst: 163a8c0 | 
|  | src: 163a8c0 dst: 163a8c0 | 
|  | src: 163a8c0 dst: 163a8c0 | 
|  | src: 163a8c0 dst: 163a8c0" || exit 1 | 
|  | remove_prog in | 
|  | } | 
|  |  | 
|  | function test_cb { | 
|  | test_start "test_cb on lwt $1" | 
|  | install_test $1 test_cb | 
|  | ping -c 3 $IPVETH1 || { | 
|  | failure "test_cb ${1}: packets are dropped" | 
|  | } | 
|  | match_trace "$(get_trace)" " | 
|  | cb0: 0 cb1: 0 cb2: 0 | 
|  | cb3: 0 cb4: 0 | 
|  | cb0: 0 cb1: 0 cb2: 0 | 
|  | cb3: 0 cb4: 0 | 
|  | cb0: 0 cb1: 0 cb2: 0 | 
|  | cb3: 0 cb4: 0" || exit 1 | 
|  | remove_prog $1 | 
|  | } | 
|  |  | 
|  | function test_cb_in { | 
|  | test_start "test_cb on lwt in" | 
|  | install_test in test_cb | 
|  | ping -c 3 $IP_LOCAL || { | 
|  | failure "test_cb in: packets are dropped" | 
|  | } | 
|  | # We will both request & reply packets as the packets will | 
|  | # be from $IP_LOCAL => $IP_LOCAL | 
|  | match_trace "$(get_trace)" " | 
|  | cb0: 0 cb1: 0 cb2: 0 | 
|  | cb3: 0 cb4: 0 | 
|  | cb0: 0 cb1: 0 cb2: 0 | 
|  | cb3: 0 cb4: 0 | 
|  | cb0: 0 cb1: 0 cb2: 0 | 
|  | cb3: 0 cb4: 0 | 
|  | cb0: 0 cb1: 0 cb2: 0 | 
|  | cb3: 0 cb4: 0 | 
|  | cb0: 0 cb1: 0 cb2: 0 | 
|  | cb3: 0 cb4: 0 | 
|  | cb0: 0 cb1: 0 cb2: 0 | 
|  | cb3: 0 cb4: 0" || exit 1 | 
|  | remove_prog in | 
|  | } | 
|  |  | 
|  | function test_drop_all { | 
|  | test_start "test_drop_all on lwt $1" | 
|  | install_test $1 drop_all | 
|  | ping -c 3 $IPVETH1 && { | 
|  | failure "test_drop_all ${1}: Unexpected success of ping" | 
|  | } | 
|  | match_trace "$(get_trace)" " | 
|  | dropping with: 2 | 
|  | dropping with: 2 | 
|  | dropping with: 2" || exit 1 | 
|  | remove_prog $1 | 
|  | } | 
|  |  | 
|  | function test_drop_all_in { | 
|  | test_start "test_drop_all on lwt in" | 
|  | install_test in drop_all | 
|  | ping -c 3 $IP_LOCAL && { | 
|  | failure "test_drop_all in: Unexpected success of ping" | 
|  | } | 
|  | match_trace "$(get_trace)" " | 
|  | dropping with: 2 | 
|  | dropping with: 2 | 
|  | dropping with: 2" || exit 1 | 
|  | remove_prog in | 
|  | } | 
|  |  | 
|  | function test_push_ll_and_redirect { | 
|  | test_start "test_push_ll_and_redirect on lwt xmit" | 
|  | install_test xmit push_ll_and_redirect | 
|  | ping -c 3 $IPVETH1 || { | 
|  | failure "Redirected packets appear to be dropped" | 
|  | } | 
|  | match_trace "$(get_trace)" " | 
|  | redirected to $DST_IFINDEX | 
|  | redirected to $DST_IFINDEX | 
|  | redirected to $DST_IFINDEX" || exit 1 | 
|  | remove_prog xmit | 
|  | } | 
|  |  | 
|  | function test_no_l2_and_redirect { | 
|  | test_start "test_no_l2_and_redirect on lwt xmit" | 
|  | install_test xmit fill_garbage_and_redirect | 
|  | ping -c 3 $IPVETH1 && { | 
|  | failure "Unexpected success despite lack of L2 header" | 
|  | } | 
|  | match_trace "$(get_trace)" " | 
|  | redirected to $DST_IFINDEX | 
|  | redirected to $DST_IFINDEX | 
|  | redirected to $DST_IFINDEX" || exit 1 | 
|  | remove_prog xmit | 
|  | } | 
|  |  | 
|  | function test_rewrite { | 
|  | test_start "test_rewrite on lwt xmit" | 
|  | install_test xmit test_rewrite | 
|  | ping -c 3 $IPVETH1 || { | 
|  | failure "Rewritten packets appear to be dropped" | 
|  | } | 
|  | match_trace "$(get_trace)" " | 
|  | out: rewriting from 2fea8c0 to 3fea8c0 | 
|  | out: rewriting from 2fea8c0 to 3fea8c0 | 
|  | out: rewriting from 2fea8c0 to 3fea8c0" || exit 1 | 
|  | remove_prog out | 
|  | } | 
|  |  | 
|  | function test_fill_garbage { | 
|  | test_start "test_fill_garbage on lwt xmit" | 
|  | install_test xmit fill_garbage | 
|  | ping -c 3 $IPVETH1 && { | 
|  | failure "test_drop_all ${1}: Unexpected success of ping" | 
|  | } | 
|  | match_trace "$(get_trace)" " | 
|  | Set initial 96 bytes of header to FF | 
|  | Set initial 96 bytes of header to FF | 
|  | Set initial 96 bytes of header to FF" || exit 1 | 
|  | remove_prog xmit | 
|  | } | 
|  |  | 
|  | function test_netperf_nop { | 
|  | test_start "test_netperf_nop on lwt xmit" | 
|  | install_test xmit nop | 
|  | netperf -H $IPVETH1 -t TCP_STREAM || { | 
|  | failure "packets appear to be dropped" | 
|  | } | 
|  | match_trace "$(get_trace)" ""|| exit 1 | 
|  | remove_prog xmit | 
|  | } | 
|  |  | 
|  | function test_netperf_redirect { | 
|  | test_start "test_netperf_redirect on lwt xmit" | 
|  | install_test xmit push_ll_and_redirect_silent | 
|  | netperf -H $IPVETH1 -t TCP_STREAM || { | 
|  | failure "Rewritten packets appear to be dropped" | 
|  | } | 
|  | match_trace "$(get_trace)" ""|| exit 1 | 
|  | remove_prog xmit | 
|  | } | 
|  |  | 
|  | cleanup | 
|  | setup_one_veth $NS1 $VETH0 $VETH1 $IPVETH0 $IPVETH1 $IPVETH1b | 
|  | setup_one_veth $NS2 $VETH2 $VETH3 $IPVETH2 $IPVETH3 | 
|  | ip netns exec $NS1 netserver | 
|  | echo 1 > ${TRACE_ROOT}/tracing_on | 
|  | echo nocontext-info > ${TRACE_ROOT}/trace_options | 
|  |  | 
|  | DST_MAC=$(lookup_mac $VETH1 $NS1) | 
|  | SRC_MAC=$(lookup_mac $VETH0) | 
|  | DST_IFINDEX=$(cat /sys/class/net/$VETH0/ifindex) | 
|  |  | 
|  | CLANG_OPTS="-O2 --target=bpf -I ../include/" | 
|  | CLANG_OPTS+=" -DSRC_MAC=$SRC_MAC -DDST_MAC=$DST_MAC -DDST_IFINDEX=$DST_IFINDEX" | 
|  | clang $CLANG_OPTS -c $PROG_SRC -o $BPF_PROG | 
|  |  | 
|  | test_ctx_xmit | 
|  | test_ctx_out | 
|  | test_ctx_in | 
|  | test_data "xmit" | 
|  | test_data "out" | 
|  | test_data_in | 
|  | test_cb "xmit" | 
|  | test_cb "out" | 
|  | test_cb_in | 
|  | test_drop_all "xmit" | 
|  | test_drop_all "out" | 
|  | test_drop_all_in | 
|  | test_rewrite | 
|  | test_push_ll_and_redirect | 
|  | test_no_l2_and_redirect | 
|  | test_fill_garbage | 
|  | test_netperf_nop | 
|  | test_netperf_redirect | 
|  |  | 
|  | cleanup | 
|  | echo 0 > ${TRACE_ROOT}/tracing_on | 
|  | echo $CONTEXT_INFO > ${TRACE_ROOT}/trace_options | 
|  | exit 0 |