#!/bin/bash

# NFT_TEST_REQUIRES(NFT_TEST_HAVE_socat)

. $NFT_TEST_LIBRARY_FILE

cleanup()
{
	for i in $C $S;do
		kill $(ip netns pid $i) 2>/dev/null
		ip netns del $i
	done
}
trap cleanup EXIT

rnd=$(mktemp -u XXXXXXXX)
C="ratelimit-client-$rnd"
S="ratelimit-server-$rnd"

ip_sc=10.167.1.1
ip_cs=10.167.1.2
ip1_cs=10.167.1.3

ip netns add $S
ip netns add $C

ip link add s_c netns $S type veth peer name c_s netns $C
ip -net $S link set s_c up
ip -net $C link set c_s up
ip -net $S link set lo up
ip -net $C link set lo up
ip -net $S addr add ${ip_sc}/24  dev s_c
ip -net $C addr add ${ip_cs}/24  dev c_s
ip -net $C addr add ${ip1_cs}/24 dev c_s
ip netns exec $C ping ${ip_sc} -c1
assert_pass "topo initialization"

ip netns exec $S $NFT -f - <<EOF
table ip filter {
	set icmp1 {
		type ipv4_addr
		size 65535
		flags dynamic,timeout
	}

	set http1 {
		type inet_service . ipv4_addr
		size 65535
		flags dynamic
	}

	chain input {
		type filter hook input priority filter; policy accept;
		ip protocol icmp counter jump in_icmp
		ip protocol tcp  counter jump in_tcp
	}
	chain in_tcp {
		iifname "s_c" tcp dport 80 ct state new add @http1 { tcp dport . ip saddr limit rate over 1/minute burst 5 packets } counter reject
		iifname "s_c" tcp dport 80 counter accept
	}

	chain in_icmp {
		iifname "s_c" ip protocol icmp counter update @icmp1 { ip saddr timeout 3s limit rate 1/second burst 5 packets } counter accept
		iifname "s_c" ip protocol icmp counter reject
	}

}
EOF
assert_pass "Apply ruleset"

# icmp test
ip netns exec $C ping -W 1 ${ip_sc} -c 5 -f -I ${ip_cs} | grep -q '5 received'
assert_pass "saddr1, burst 5 accept"

ip netns exec $C ping -W 1 ${ip_sc} -c 5 -f -I ${ip1_cs} | grep -q '5 received'
assert_pass "saddr2, burst 5 accept"

ip netns exec $C ping -W 1 ${ip_sc} -c 1 -f -I ${ip_cs} | grep -q '1 received'
assert_fail "saddr1, burst 5 up to limit, reject"

sleep 3
ip netns exec $C ping -W 1 ${ip_sc} -c 5 -f -I ${ip_cs} | grep -q '5 received'
assert_pass "saddr1, element timeout,burst 5 pass again"

ip netns exec $C ping -W 1 ${ip_sc} -c 1 -f -I ${ip_cs} | grep -q '1 received'
assert_fail "saddr1, burst 5 up to limit"

ip netns exec $C ping -W 1 ${ip_sc} -c 6 -i 1 -I ${ip1_cs} | grep -q '6 received'
assert_pass "saddr2, 6s test, limit rate 1/second accept"

ip netns exec $C ping -W 1 ${ip_sc} -c 4 -f -I ${ip1_cs} | grep -q '4 received'
assert_pass "saddr2, burst 4 accept"

sleep 2
ip netns exec $C ping -W 1 ${ip_sc} -c 2 -f -I ${ip1_cs} | grep -q '2 received'
assert_pass "saddr2, burst 2 sleep 2, accept"

sleep 2
ip netns exec $C ping -W 1 ${ip_sc} -c 2 -f -I ${ip1_cs} | grep -q '2 received'
assert_pass "saddr2, burst 2 sleep 2, accept"

ip netns exec $C ping -W 1 ${ip_sc} -c 1 -f -I ${ip1_cs} | grep -q '1 received'
assert_fail "saddr2, limit rate 1/second up to limit, reject"


# tcp test
ip netns exec $S socat TCP-LISTEN:80,reuseaddr,fork - > /dev/null &
wait_local_port_listen $S 80 tcp

for port in {1..5};do
	ip netns exec $C socat -u - TCP:${ip_sc}:80,connect-timeout=1 <<< 'AAA'
	assert_pass "tcp connection burst 5 accept"
done
ip netns exec $C socat -u - TCP:${ip_sc}:80,reuseport,connect-timeout=1 <<< 'AAA'
assert_fail "tcp connection burst 5 up to limit reject"

ip netns exec $S $NFT flush chain filter in_tcp
assert_pass result "flush chain"

ip netns exec $S $NFT flush set filter http1
assert_pass result "flush set"

ip netns exec $S $NFT add rule filter in_tcp iifname s_c tcp dport 80 ct state new add @http1 { tcp dport . ip saddr limit rate over 1/second burst 1 packets} counter reject
assert_pass result "add rule limit rate over 1/second burst 1"
ip netns exec $S $NFT add rule filter in_tcp iifname s_c tcp dport 80 counter accept

sleep 1
ip netns exec $C socat -u - TCP:${ip_sc}:80,reuseport,connect-timeout=1 <<< 'AAA'
assert_pass result "tcp connection limit rate 1/sec burst 1 accept"

ip netns exec $C socat -u - TCP:${ip_sc}:80,reuseport,connect-timeout=1 <<< 'AAA'
assert_fail result "tcp connection limit rate 1/sec burst 1 reject"

sleep 1
ip netns exec $C socat -u - TCP:${ip_sc}:80,reuseport,connect-timeout=1 <<< 'AAA'
assert_pass result "tcp connection limit rate 1/sec burst 1 accept"
