#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
#
# Test devlink-trap tunnel drops and exceptions functionality over mlxsw.
# Check all traps to make sure they are triggered under the right
# conditions.

# +------------------------+
# | H1 (vrf)               |
# |    + $h1               |
# |    | 2001:db8:1::1/64  |
# +----|-------------------+
#      |
# +----|----------------------------------------------------------------------+
# | SW |                                                                      |
# | +--|--------------------------------------------------------------------+ |
# | |  + $swp1                   BR1 (802.1d)                               | |
# | |                                                                       | |
# | |  + vx1 (vxlan)                                                        | |
# | |    local 2001:db8:3::1                                                | |
# | |    id 1000 dstport $VXPORT                                            | |
# | +-----------------------------------------------------------------------+ |
# |                                                                           |
# |    + $rp1                                                                 |
# |    | 2001:db8:3::1/64                                                     |
# +----|----------------------------------------------------------------------+
#      |
# +----|--------------------------------------------------------+
# |    |                                             VRF2       |
# |    + $rp2                                                   |
# |      2001:db8:3::2/64                                       |
# |                                                             |
# +-------------------------------------------------------------+

lib_dir=$(dirname $0)/../../../net/forwarding

ALL_TESTS="
	decap_error_test
	overlay_smac_is_mc_test
"

NUM_NETIFS=4
source $lib_dir/lib.sh
source $lib_dir/tc_common.sh
source $lib_dir/devlink_lib.sh

: ${VXPORT:=4789}
export VXPORT

h1_create()
{
	simple_if_init $h1 2001:db8:1::1/64
}

h1_destroy()
{
	simple_if_fini $h1 2001:db8:1::1/64
}

switch_create()
{
	ip link add name br1 type bridge vlan_filtering 0 mcast_snooping 0
	# Make sure the bridge uses the MAC address of the local port and not
	# that of the VxLAN's device.
	ip link set dev br1 address $(mac_get $swp1)
	ip link set dev br1 up

	tc qdisc add dev $swp1 clsact
	ip link set dev $swp1 master br1
	ip link set dev $swp1 up

	ip link add name vx1 type vxlan id 1000 local 2001:db8:3::1 \
		dstport "$VXPORT" nolearning udp6zerocsumrx udp6zerocsumtx \
		tos inherit ttl 100
	ip link set dev vx1 master br1
	ip link set dev vx1 up

	ip link set dev $rp1 up
	ip address add dev $rp1 2001:db8:3::1/64
}

switch_destroy()
{
	ip address del dev $rp1 2001:db8:3::1/64
	ip link set dev $rp1 down

	ip link set dev vx1 down
	ip link set dev vx1 nomaster
	ip link del dev vx1

	ip link set dev $swp1 down
	ip link set dev $swp1 nomaster
	tc qdisc del dev $swp1 clsact

	ip link set dev br1 down
	ip link del dev br1
}

vrf2_create()
{
	simple_if_init $rp2 2001:db8:3::2/64
}

vrf2_destroy()
{
	simple_if_fini $rp2 2001:db8:3::2/64
}

setup_prepare()
{
	h1=${NETIFS[p1]}
	swp1=${NETIFS[p2]}

	rp1=${NETIFS[p3]}
	rp2=${NETIFS[p4]}

	vrf_prepare
	forwarding_enable
	h1_create
	switch_create
	vrf2_create
}

cleanup()
{
	pre_cleanup

	vrf2_destroy
	switch_destroy
	h1_destroy
	forwarding_restore
	vrf_cleanup
}

ecn_payload_get()
{
	local dest_mac=$(mac_get $h1)
	local saddr="20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:03"
	local daddr="20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:01"
	p=$(:
		)"08:"$(                      : VXLAN flags
		)"00:00:00:"$(                : VXLAN reserved
		)"00:03:e8:"$(                : VXLAN VNI : 1000
		)"00:"$(                      : VXLAN reserved
		)"$dest_mac:"$(               : ETH daddr
		)"00:00:00:00:00:00:"$(       : ETH saddr
		)"86:dd:"$(                   : ETH type
		)"6"$(                        : IP version
		)"0:0"$(		      : Traffic class
		)"0:00:00:"$(                 : Flow label
		)"00:08:"$(                   : Payload length
		)"3a:"$(                      : Next header
		)"04:"$(                      : Hop limit
		)"$saddr:"$(                  : IP saddr
		)"$daddr:"$(		      : IP daddr
		)"80:"$(                      : ICMPv6.type
		)"00:"$(                      : ICMPv6.code
		)"00:"$(                      : ICMPv6.checksum
		)
	echo $p
}

ecn_decap_test()
{
	local trap_name="decap_error"
	local desc=$1; shift
	local ecn_desc=$1; shift
	local outer_tos=$1; shift
	local mz_pid

	RET=0

	tc filter add dev $swp1 egress protocol ipv6 pref 1 handle 101 \
		flower src_ip 2001:db8:1::3 dst_ip 2001:db8:1::1 action pass

	rp1_mac=$(mac_get $rp1)
	payload=$(ecn_payload_get)

	ip vrf exec v$rp2 $MZ -6 $rp2 -c 0 -d 1msec -b $rp1_mac \
		-B 2001:db8:3::1 -t udp \
		sp=12345,dp=$VXPORT,tos=$outer_tos,p=$payload -q &
	mz_pid=$!

	devlink_trap_exception_test $trap_name

	tc_check_packets "dev $swp1 egress" 101 0
	check_err $? "Packets were not dropped"

	log_test "$desc: Inner ECN is not ECT and outer is $ecn_desc"

	kill $mz_pid && wait $mz_pid &> /dev/null
	tc filter del dev $swp1 egress protocol ipv6 pref 1 handle 101 flower
}

reserved_bits_payload_get()
{
	local dest_mac=$(mac_get $h1)
	local saddr="20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:03"
	local daddr="20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:01"
	p=$(:
		)"08:"$(                      : VXLAN flags
		)"01:00:00:"$(                : VXLAN reserved
		)"00:03:e8:"$(                : VXLAN VNI : 1000
		)"00:"$(                      : VXLAN reserved
		)"$dest_mac:"$(               : ETH daddr
		)"00:00:00:00:00:00:"$(       : ETH saddr
		)"86:dd:"$(                   : ETH type
		)"6"$(                        : IP version
		)"0:0"$(		      : Traffic class
		)"0:00:00:"$(                 : Flow label
		)"00:08:"$(                   : Payload length
		)"3a:"$(                      : Next header
		)"04:"$(                      : Hop limit
		)"$saddr:"$(                  : IP saddr
		)"$daddr:"$(		      : IP daddr
		)"80:"$(                      : ICMPv6.type
		)"00:"$(                      : ICMPv6.code
		)"00:"$(                      : ICMPv6.checksum
		)
	echo $p
}

short_payload_get()
{
        dest_mac=$(mac_get $h1)
        p=$(:
		)"08:"$(                      : VXLAN flags
		)"00:00:00:"$(                : VXLAN reserved
		)"00:03:e8:"$(                : VXLAN VNI : 1000
		)"00:"$(                      : VXLAN reserved
		)"$dest_mac:"$(               : ETH daddr
		)"00:00:00:00:00:00:"$(       : ETH saddr
		)
        echo $p
}

corrupted_packet_test()
{
	local trap_name="decap_error"
	local desc=$1; shift
	local payload_get=$1; shift
	local mz_pid

	RET=0

	# In case of too short packet, there is no any inner packet,
	# so the matching will always succeed
	tc filter add dev $swp1 egress protocol ipv6 pref 1 handle 101 \
		flower skip_hw src_ip 2001:db8:3::1 dst_ip 2001:db8:1::1 \
		action pass

	rp1_mac=$(mac_get $rp1)
	payload=$($payload_get)
	ip vrf exec v$rp2 $MZ -6 $rp2 -c 0 -d 1msec -b $rp1_mac \
		-B 2001:db8:3::1 -t udp sp=12345,dp=$VXPORT,p=$payload -q &
	mz_pid=$!

	devlink_trap_exception_test $trap_name

	tc_check_packets "dev $swp1 egress" 101 0
	check_err $? "Packets were not dropped"

	log_test "$desc"

	kill $mz_pid && wait $mz_pid &> /dev/null
	tc filter del dev $swp1 egress protocol ipv6 pref 1 handle 101 flower
}

decap_error_test()
{
	ecn_decap_test "Decap error" "ECT(1)" 01
	ecn_decap_test "Decap error" "ECT(0)" 02
	ecn_decap_test "Decap error" "CE" 03

	corrupted_packet_test "Decap error: Reserved bits in use" \
		"reserved_bits_payload_get"
	corrupted_packet_test "Decap error: Too short inner packet" \
		"short_payload_get"
}

mc_smac_payload_get()
{
	local dest_mac=$(mac_get $h1)
	local source_mac="01:02:03:04:05:06"
	local saddr="20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:03"
	local daddr="20:01:0d:b8:00:01:00:00:00:00:00:00:00:00:00:01"
	p=$(:
		)"08:"$(                      : VXLAN flags
		)"00:00:00:"$(                : VXLAN reserved
		)"00:03:e8:"$(                : VXLAN VNI : 1000
		)"00:"$(                      : VXLAN reserved
		)"$dest_mac:"$(               : ETH daddr
		)"$source_mac:"$(	      : ETH saddr
		)"86:dd:"$(                   : ETH type
		)"6"$(                        : IP version
		)"0:0"$(		      : Traffic class
		)"0:00:00:"$(                 : Flow label
		)"00:08:"$(                   : Payload length
		)"3a:"$(                      : Next header
		)"04:"$(                      : Hop limit
		)"$saddr:"$(                  : IP saddr
		)"$daddr:"$(		      : IP daddr
		)"80:"$(                      : ICMPv6.type
		)"00:"$(                      : ICMPv6.code
		)"00:"$(                      : ICMPv6.checksum
		)
	echo $p
}

overlay_smac_is_mc_test()
{
	local trap_name="overlay_smac_is_mc"
	local mz_pid

	RET=0

	# The matching will be checked on devlink_trap_drop_test()
	# and the filter will be removed on devlink_trap_drop_cleanup()
	tc filter add dev $swp1 egress protocol ipv6 pref 1 handle 101 \
		flower src_mac 01:02:03:04:05:06 action pass

	rp1_mac=$(mac_get $rp1)
	payload=$(mc_smac_payload_get)

	ip vrf exec v$rp2 $MZ -6 $rp2 -c 0 -d 1msec -b $rp1_mac \
		-B 2001:db8:3::1 -t udp sp=12345,dp=$VXPORT,p=$payload -q &
	mz_pid=$!

	devlink_trap_drop_test $trap_name $swp1 101

	log_test "Overlay source MAC is multicast"

	devlink_trap_drop_cleanup $mz_pid $swp1 "ipv6" 1 101
}

trap cleanup EXIT

setup_prepare
setup_wait
tests_run

exit $EXIT_STATUS