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

# The script is mostly generic, with the exception of the
# ethtool per-TC counter names ("rx_green_prio_${tc}")

WAIT_TIME=1
NUM_NETIFS=4
STABLE_MAC_ADDRS=yes
NETIF_CREATE=no
lib_dir=$(dirname $0)/../../../net/forwarding
source $lib_dir/tc_common.sh
source $lib_dir/lib.sh

require_command dcb

h1=${NETIFS[p1]}
swp1=${NETIFS[p2]}
swp2=${NETIFS[p3]}
h2=${NETIFS[p4]}

H1_IPV4="192.0.2.1"
H2_IPV4="192.0.2.2"
H1_IPV6="2001:db8:1::1"
H2_IPV6="2001:db8:1::2"

h1_create()
{
	simple_if_init $h1 $H1_IPV4/24 $H1_IPV6/64
}

h1_destroy()
{
	simple_if_fini $h1 $H1_IPV4/24 $H1_IPV6/64
}

h2_create()
{
	simple_if_init $h2 $H2_IPV4/24 $H2_IPV6/64
}

h2_destroy()
{
	simple_if_fini $h2 $H2_IPV4/24 $H2_IPV6/64
}

h1_vlan_create()
{
	local vid=$1

	vlan_create $h1 $vid
	simple_if_init $h1.$vid $H1_IPV4/24 $H1_IPV6/64
	ip link set $h1.$vid type vlan \
		egress-qos-map 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7 \
		ingress-qos-map 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
}

h1_vlan_destroy()
{
	local vid=$1

	simple_if_fini $h1.$vid $H1_IPV4/24 $H1_IPV6/64
	vlan_destroy $h1 $vid
}

h2_vlan_create()
{
	local vid=$1

	vlan_create $h2 $vid
	simple_if_init $h2.$vid $H2_IPV4/24 $H2_IPV6/64
	ip link set $h2.$vid type vlan \
		egress-qos-map 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7 \
		ingress-qos-map 0:0 1:1 2:2 3:3 4:4 5:5 6:6 7:7
}

h2_vlan_destroy()
{
	local vid=$1

	simple_if_fini $h2.$vid $H2_IPV4/24 $H2_IPV6/64
	vlan_destroy $h2 $vid
}

vlans_prepare()
{
	h1_vlan_create 100
	h2_vlan_create 100

	tc qdisc add dev ${h1}.100 clsact
	tc filter add dev ${h1}.100 egress protocol ipv4 \
		flower ip_proto icmp action skbedit priority 3
	tc filter add dev ${h1}.100 egress protocol ipv6 \
		flower ip_proto icmpv6 action skbedit priority 3
}

vlans_destroy()
{
	tc qdisc del dev ${h1}.100 clsact

	h1_vlan_destroy 100
	h2_vlan_destroy 100
}

switch_create()
{
	ip link set ${swp1} up
	ip link set ${swp2} up

	# Ports should trust VLAN PCP even with vlan_filtering=0
	ip link add br0 type bridge
	ip link set ${swp1} master br0
	ip link set ${swp2} master br0
	ip link set br0 up
}

switch_destroy()
{
	ip link del br0
}

setup_prepare()
{
	vrf_prepare

	h1_create
	h2_create
	switch_create
}

cleanup()
{
	pre_cleanup

	h2_destroy
	h1_destroy
	switch_destroy

	vrf_cleanup
}

dscp_cs_to_tos()
{
	local dscp_cs=$1

	# https://datatracker.ietf.org/doc/html/rfc2474
	# 4.2.2.1  The Class Selector Codepoints
	echo $((${dscp_cs} << 5))
}

run_test()
{
	local test_name=$1; shift
	local if_name=$1; shift
	local tc=$1; shift
	local tos=$1; shift
	local counter_name="rx_green_prio_${tc}"
	local ipv4_before
	local ipv4_after
	local ipv6_before
	local ipv6_after

	ipv4_before=$(ethtool_stats_get ${swp1} "${counter_name}")
	ping_do ${if_name} $H2_IPV4 "-Q ${tos}"
	ipv4_after=$(ethtool_stats_get ${swp1} "${counter_name}")

	if [ $((${ipv4_after} - ${ipv4_before})) -lt ${PING_COUNT} ]; then
		RET=1
	else
		RET=0
	fi
	log_test "IPv4 ${test_name}"

	ipv6_before=$(ethtool_stats_get ${swp1} "${counter_name}")
	ping_do ${if_name} $H2_IPV6 "-Q ${tos}"
	ipv6_after=$(ethtool_stats_get ${swp1} "${counter_name}")

	if [ $((${ipv6_after} - ${ipv6_before})) -lt ${PING_COUNT} ]; then
		RET=1
	else
		RET=0
	fi
	log_test "IPv6 ${test_name}"
}

port_default_prio_get()
{
	local if_name=$1
	local prio

	prio="$(dcb -j app show dev ${if_name} default-prio | \
		jq '.default_prio[]')"
	if [ -z "${prio}" ]; then
		prio=0
	fi

	echo ${prio}
}

test_port_default()
{
	local orig=$(port_default_prio_get ${swp1})
	local dmac=$(mac_get ${h2})

	dcb app replace dev ${swp1} default-prio 5

	run_test "Port-default QoS classification" ${h1} 5 0

	dcb app replace dev ${swp1} default-prio ${orig}
}

test_vlan_pcp()
{
	vlans_prepare

	run_test "Trusted VLAN PCP QoS classification" ${h1}.100 3 0

	vlans_destroy
}

test_ip_dscp()
{
	local port_default=$(port_default_prio_get ${swp1})
	local tos=$(dscp_cs_to_tos 4)

	dcb app add dev ${swp1} dscp-prio CS4:4
	run_test "Trusted DSCP QoS classification" ${h1} 4 ${tos}
	dcb app del dev ${swp1} dscp-prio CS4:4

	vlans_prepare
	run_test "Untrusted DSCP QoS classification follows VLAN PCP" \
		${h1}.100 3 ${tos}
	vlans_destroy

	run_test "Untrusted DSCP QoS classification follows port default" \
		${h1} ${port_default} ${tos}
}

trap cleanup EXIT

ALL_TESTS="
	test_port_default
	test_vlan_pcp
	test_ip_dscp
"

setup_prepare
setup_wait

tests_run

exit $EXIT_STATUS