# this custom script sets FILTER_MARK to specified source ips

# NOTE !!! SCRIPT REQUIRES FILTER_MARK VAR IN CONFIG FILE !!!
# NOTE !!! WITHOUT FILTER_MARK IT DOES NOTHING !!!

# NOTE !!! ON NON-OPENWRT SYSTEMS SCRIPT REQUIRES IFACE_LAN VAR IN CONFIG FILE !!!

# can override in config :
# LAN ip/cidr list to be fooled. elements are space separated
FILTER_LAN_IP="${FILTER_LAN_IP:-192.168.0.0/16}"
FILTER_LAN_IP6="${FILTER_LAN_IP6:-fc00::/7}"
# allow fooling from local system (0|1) ?
FILTER_LAN_ALLOW_OUTPUT="${FILTER_LAN_ALLOW_OUTPUT:-1}"

FILTER_LAN_SET="lanfilter"
FILTER_LAN_SET6="${FILTER_LAN_SET}6"
FILTER_LAN_IPSET_SIZE=${FILTER_LAN_IPSET_SIZE:-256}
FILTER_LAN_IPSET_OPT="${FILTER_LAN_IPSET_OPT:-hash:net hashsize 8192 maxelem $FILTER_LAN_IPSET_SIZE}"

filter_mark_check()
{
	[ -n "$FILTER_MARK" ] || {
		echo "WARNING ! lan filter cannot work without FILTER_MARK set in config"
		return 1
	}
	[ "$DISABLE_IPV4" = 1 -a "$DISABLE_IPV6" = 1 ] && return 1
	return 0
}

zapret_custom_firewall()
{
	# $1 - 1 - run, 0 - stop

	filter_mark_check || return

	local subnet lanifs rule
	local setmark="-j MARK --set-mark $FILTER_MARK/$FILTER_MARK"
	local filt4="-m set --match-set $FILTER_LAN_SET src"
	local filt6="-m set --match-set $FILTER_LAN_SET6 src"

	get_lanif lanifs

	[ "$DISABLE_IPV4" != 1 ] && {
		[ "$FILTER_LAN_ALLOW_OUTPUT" = 1 ] && {
			ipt_print_op $1 "$setmark" "filter output"
			ipt_add_del $1 OUTPUT -t mangle $setmark
		}
		[ -n "$lanifs" ] && {
			[ "$1" = 1 ] && {
				ipset create $FILTER_LAN_SET $FILTER_LAN_IPSET_OPT family inet 2>/dev/null
				ipset flush $FILTER_LAN_SET
				for subnet in $FILTER_LAN_IP; do
					echo add $FILTER_LAN_SET $subnet
				done | ipset -! restore
			}
			for lan in $lanifs; do
				rule="-i $lan $filt4 $setmark"
				ipt_print_op $1 "$rule" "filter forward"
				ipt_add_del $1 FORWARD -t mangle $rule
			done
		}
	}
	[ "$DISABLE_IPV6" != 1 ] && {
		[ "$FILTER_LAN_ALLOW_OUTPUT" = 1 ] && {
			ipt_print_op $1 "$setmark" "filter output" 6
			ipt6_add_del $1 OUTPUT -t mangle $setmark
		}
		[ -n "$lanifs" ] && {
			[ "$1" = 1 ] && {
				ipset create $FILTER_LAN_SET6 $FILTER_LAN_IPSET_OPT family inet6 2>/dev/null
				ipset flush $FILTER_LAN_SET6
				for subnet in $FILTER_LAN_IP6; do
					echo add $FILTER_LAN_SET6 $subnet
				done | ipset -! restore
			}
			for lan in $lanifs; do
				rule="-i $lan $filt6 $setmark"
				ipt_print_op $1 "$rule" "filter forward" 6
				ipt6_add_del $1 FORWARD -t mangle $rule
			done
		}
	}

	[ "$1" = 1 ] || {
		ipset destroy $FILTER_LAN_SET 2>/dev/null
		ipset destroy $FILTER_LAN_SET6 2>/dev/null
	}
}

zapret_custom_firewall_nft()
{
	filter_mark_check || return

	local subnets rule
	local setmark="meta mark set meta mark or $FILTER_MARK"
	local filt4="ip saddr == @$FILTER_LAN_SET"
	local filt6="ip6 saddr == @$FILTER_LAN_SET6"
	local lanif="iifname @lanif"

	nft_add_chain forward_lan_filter "type filter hook forward priority mangle;"
	nft_flush_chain forward_lan_filter

	if [ "$FILTER_LAN_ALLOW_OUTPUT" = 1 ]; then
		nft_add_chain output_lan_filter "type filter hook output priority mangle;"
		nft_flush_chain output_lan_filter
		nft_print_op "$setmark" "filter output" "4+6"
		nft_add_rule output_lan_filter $setmark
	else
		nft_del_chain output_lan_filter 2>/dev/null
	fi

	[ "$DISABLE_IPV4" != 1 ] && {
		make_comma_list subnets $FILTER_LAN_IP
		nft_create_set $FILTER_LAN_SET "type ipv4_addr; size $FILTER_LAN_IPSET_SIZE; auto-merge; flags interval;"
		nft_flush_set $FILTER_LAN_SET
		nft_add_set_element $FILTER_LAN_SET "$subnets"

		rule="$lanif $filt4 $setmark"
		nft_print_op "$rule" "filter forward" "4"
		nft_add_rule forward_lan_filter $rule
	}
	[ "$DISABLE_IPV6" != 1 ] && {
		make_comma_list subnets $FILTER_LAN_IP6
		nft_create_set $FILTER_LAN_SET6 "type ipv6_addr; size $FILTER_LAN_IPSET_SIZE; auto-merge; flags interval;"
		nft_flush_set $FILTER_LAN_SET6
		nft_add_set_element $FILTER_LAN_SET6 "$subnets"

		rule="$lanif $filt6 $setmark"
		nft_print_op "$rule" "filter forward" "6"
		nft_add_rule forward_lan_filter $rule
	}
}


zapret_custom_firewall_nft_flush()
{
	# this function is called after all nft fw rules are deleted
	# however sets are not deleted. it's desired to clear sets here.

	nft_del_chain forward_lan_filter 2>/dev/null
	nft_del_chain output_lan_filter 2>/dev/null

	nft_del_set $FILTER_LAN_SET 2>/dev/null
	nft_del_set $FILTER_LAN_SET6 2>/dev/null
}
