#!/bin/bash
# SPDX-License-Identifier: GPL-2.0

# Test routing after VXLAN decapsulation and verify that the order of
# configuration does not impact switch behavior. Verify that RIF is added
# correctly for existing mapping and that new mapping uses the correct RIF.

# +---------------------------+
# |                        H1 |
# |    + $h1                  |
# |    | 192.0.2.1/28         |
# +----|----------------------+
#      |
# +----|----------------------------------------------------------------------+
# | SW |                                                                      |
# | +--|--------------------------------------------------------------------+ |
# | |  + $swp1                         br1                                  | |
# | |     vid 10 pvid untagged                                              | |
# | |                                                                       | |
# | |                                                                       | |
# | |                                            + vx4001                   | |
# | |                                              local 192.0.2.17         | |
# | |                                              remote 192.0.2.18        | |
# | |                                              id 104001                | |
# | |                                              dstport $VXPORT          | |
# | |                                              vid 4001 pvid untagged   | |
# | |                                                                       | |
# | +----------------------------------+------------------------------------+ |
# |                                    |                                      |
# | +----------------------------------|------------------------------------+ |
# | |                                  |                                    | |
# | |  +-------------------------------+---------------------------------+  | |
# | |  |                                                                 |  | |
# | |  + vlan10                                                 vlan4001 +  | |
# | |    192.0.2.2/28                                                       | |
# | |                                                                       | |
# | |                               vrf-green                               | |
# | +-----------------------------------------------------------------------+ |
# |                                                                           |
# |    + $rp1                                       +lo                       |
# |    | 198.51.100.1/24                             192.0.2.17/32            |
# +----|----------------------------------------------------------------------+
#      |
# +----|--------------------------------------------------------+
# |    |                                             v$rp2      |
# |    + $rp2                                                   |
# |      198.51.100.2/24                                        |
# |                                                             |
# +-------------------------------------------------------------+

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

ALL_TESTS="
	vni_fid_map_rif
	rif_vni_fid_map
"

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 192.0.2.1/28
}

h1_destroy()
{
	simple_if_fini $h1 192.0.2.1/28
}

switch_create()
{
	ip link add name br1 type bridge vlan_filtering 1 vlan_default_pvid 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

	ip link set dev $rp1 up
	ip address add dev $rp1 198.51.100.1/24

	ip link set dev $swp1 master br1
	ip link set dev $swp1 up
	bridge vlan add vid 10 dev $swp1 pvid untagged

	tc qdisc add dev $swp1 clsact

	ip link add name vx4001 type vxlan id 104001 \
		local 192.0.2.17 dstport $VXPORT \
		nolearning noudpcsum tos inherit ttl 100
	ip link set dev vx4001 up

	ip link set dev vx4001 master br1

	ip address add 192.0.2.17/32 dev lo

	# Create SVIs.
	vrf_create "vrf-green"
	ip link set dev vrf-green up

	ip link add link br1 name vlan10 up master vrf-green type vlan id 10

	# Replace neighbor to avoid 1 packet which is forwarded in software due
	# to "unresolved neigh".
	ip neigh replace dev vlan10 192.0.2.1 lladdr $(mac_get $h1)

	ip address add 192.0.2.2/28 dev vlan10

	bridge vlan add vid 10 dev br1 self
	bridge vlan add vid 4001 dev br1 self

	sysctl_set net.ipv4.conf.all.rp_filter 0
}

switch_destroy()
{
	sysctl_restore net.ipv4.conf.all.rp_filter

	bridge vlan del vid 4001 dev br1 self
	bridge vlan del vid 10 dev br1 self

	ip link del dev vlan10

	vrf_destroy "vrf-green"

	ip address del 192.0.2.17/32 dev lo

	tc qdisc del dev $swp1 clsact

	bridge vlan del vid 10 dev $swp1
	ip link set dev $swp1 down
	ip link set dev $swp1 nomaster

	ip link set dev vx4001 nomaster

	ip link set dev vx4001 down
	ip link del dev vx4001

	ip address del dev $rp1 198.51.100.1/24
	ip link set dev $rp1 down

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

vrp2_create()
{
	simple_if_init $rp2 198.51.100.2/24

	ip route add 192.0.2.17/32 vrf v$rp2 nexthop via 198.51.100.1
}

vrp2_destroy()
{
	ip route del 192.0.2.17/32 vrf v$rp2 nexthop via 198.51.100.1

	simple_if_fini $rp2 198.51.100.2/24
}

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

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

	vrf_prepare
	forwarding_enable

	h1_create
	switch_create

	vrp2_create
}

cleanup()
{
	pre_cleanup

	vrp2_destroy

	switch_destroy
	h1_destroy

	forwarding_restore
	vrf_cleanup
}

payload_get()
{
	local dest_mac=$(mac_get vlan4001)
	local src_mac=$(mac_get $rp1)

	p=$(:
		)"08:"$(                      : VXLAN flags
		)"00:00:00:"$(                : VXLAN reserved
		)"01:96:41:"$(                : VXLAN VNI : 104001
		)"00:"$(                      : VXLAN reserved
		)"$dest_mac:"$(               : ETH daddr
		)"$src_mac:"$(                : ETH saddr
		)"08:00:"$(                   : ETH type
		)"45:"$(                      : IP version + IHL
		)"00:"$(                      : IP TOS
		)"00:54:"$(                   : IP total length
		)"3f:49:"$(                   : IP identification
		)"00:00:"$(                   : IP flags + frag off
		)"3f:"$(                      : IP TTL
		)"01:"$(                      : IP proto
		)"50:21:"$(                   : IP header csum
		)"c6:33:64:0a:"$(             : IP saddr: 198.51.100.10
		)"c0:00:02:01:"$(             : IP daddr: 192.0.2.1
	)
	echo $p
}

vlan_rif_add()
{
	rifs_occ_t0=$(devlink_resource_occ_get rifs)

	ip link add link br1 name vlan4001 up master vrf-green \
		type vlan id 4001

	rifs_occ_t1=$(devlink_resource_occ_get rifs)
	expected_rifs=$((rifs_occ_t0 + 1))

	[[ $expected_rifs -eq $rifs_occ_t1 ]]
	check_err $? "Expected $expected_rifs RIFs, $rifs_occ_t1 are used"
}

vlan_rif_del()
{
	ip link del dev vlan4001
}

vni_fid_map_rif()
{
	local rp1_mac=$(mac_get $rp1)

	RET=0

	# First add VNI->FID mapping to the FID of VLAN 4001
	bridge vlan add vid 4001 dev vx4001 pvid untagged

	# Add a RIF to the FID with VNI->FID mapping
	vlan_rif_add

	tc filter add dev $swp1 egress protocol ip pref 1 handle 101 \
		flower skip_sw dst_ip 192.0.2.1 action pass

	payload=$(payload_get)
	ip vrf exec v$rp2 $MZ $rp2 -c 10 -d 1msec -b $rp1_mac \
		-B 192.0.2.17 -A 192.0.2.18 \
		-t udp sp=12345,dp=$VXPORT,p=$payload -q

	tc_check_at_least_x_packets "dev $swp1 egress" 101 10
	check_err $? "Packets were not routed in hardware"

	log_test "Add RIF for existing VNI->FID mapping"

	tc filter del dev $swp1 egress

	bridge vlan del vid 4001 dev vx4001 pvid untagged
	vlan_rif_del
}

rif_vni_fid_map()
{
	local rp1_mac=$(mac_get $rp1)

	RET=0

	# First add a RIF to the FID of VLAN 4001
	vlan_rif_add

	# Add VNI->FID mapping to FID with a RIF
	bridge vlan add vid 4001 dev vx4001 pvid untagged

	tc filter add dev $swp1 egress protocol ip pref 1 handle 101 \
		flower skip_sw dst_ip 192.0.2.1 action pass

	payload=$(payload_get)
	ip vrf exec v$rp2 $MZ $rp2 -c 10 -d 1msec -b $rp1_mac \
		-B 192.0.2.17 -A 192.0.2.18 \
		-t udp sp=12345,dp=$VXPORT,p=$payload -q

	tc_check_at_least_x_packets "dev $swp1 egress" 101 10
	check_err $? "Packets were not routed in hardware"

	log_test "Add VNI->FID mapping for FID with a RIF"

	tc filter del dev $swp1 egress

	bridge vlan del vid 4001 dev vx4001 pvid untagged
	vlan_rif_del
}

trap cleanup EXIT

setup_prepare
setup_wait

tests_run

exit $EXIT_STATUS