#!/bin/sh

# Set REJECT as default rule if an interface is not in a zone. Only on the
# first boot (openmptcprouter.settings.version is written at its end): set on
# every upgrade, these replaced the policies the user chose.
if [ "$(uci -q get openmptcprouter.settings.version)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		set firewall.@defaults[0].input='REJECT'
		set firewall.@defaults[0].output='REJECT'
		set firewall.@defaults[0].forward='REJECT'
	EOF
fi
uci -q batch <<-EOF >/dev/null
	set firewall.@defaults[0].flow_offloading='0'
	set firewall.@defaults[0].flow_offloading_hw='0'
EOF


# An old anonymous "vpn" zone gives way to zone_vpn below. After the first boot
# zone_vpn itself is @zone[2] (after lan and wan): it was deleted and created
# again on every upgrade, losing the user's changes and the tun1-3 devices.
if [ "$(uci -q get firewall.@zone[2].name)" = "vpn" ] && [ "$(uci -q show firewall.@zone[2] | head -n 1)" != "firewall.zone_vpn=zone" ]; then
	uci -q batch <<-EOF >/dev/null
		del firewall.@zone[2]
		commit firewall
	EOF
fi

if [ "$(uci -q get firewall.@zone[1].name)" = "wan" ]; then
	uci -q batch <<-EOF >/dev/null
		rename firewall.@zone[1]="zone_wan"
		commit firewall
	EOF
fi
if [ "$(uci -q show firewall.zone_wan | grep wan6)" != "" ] && [ "$(uci -q get network.wan6)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		del_list firewall.zone_wan.network="wan6"
		commit firewall
	EOF
fi

if [ "$(uci -q show firewall.zone_wan | grep wan)" != "" ] && [ "$(uci -q get network.wan)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		del_list firewall.zone_wan.network="wan"
		commit firewall
	EOF
fi
if [ "$(uci -q get firewall.@zone[0].name)" = "lan" ]; then
	uci -q batch <<-EOF >/dev/null
		rename firewall.@zone[0]="zone_lan"
		commit firewall
	EOF
fi

if [ "$(uci -q get firewall.zone_vpn)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		set firewall.zone_vpn=zone
		set firewall.zone_vpn.name=vpn
		add_list firewall.zone_vpn.network=omrvpn
		add_list firewall.zone_vpn.network=omr6in4
		set firewall.zone_vpn.masq=1
		set firewall.zone_vpn.input=REJECT
		set firewall.zone_vpn.forward=ACCEPT
		set firewall.zone_vpn.output=ACCEPT
		commit firewall
	EOF
fi

if [ "$(uci -q get firewall.@rule[5].name)" = "Allow-ICMPv6-Input" ]; then
	uci -q batch <<-EOF >/dev/null
		del firewall.@rule[5]
		commit firewall
	EOF
fi
if [ "$(uci -q get firewall.@rule[6].name)" = "Allow-ICMPv6-Forward" ]; then
	uci -q batch <<-EOF >/dev/null
		del firewall.@rule[6]
		commit firewall
	EOF
fi
if [ "$(uci -q show firewall | grep Allow-All-Ping)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		add firewall rule
		set firewall.@rule[-1].enabled='1'
		set firewall.@rule[-1].target='ACCEPT'
		set firewall.@rule[-1].name='Allow-All-Ping'
		set firewall.@rule[-1].proto='icmp'
		set firewall.@rule[-1].dest='*'
		set firewall.@rule[-1].src='*'
		set firewall.@rule[-1].icmp_type='echo-request'
		set firewall.@rule[-1].limit='1000/sec'
		commit firewall
	EOF
fi
# Allow-All-Ping above carries src='*' AND dest='*'; fw4 puts a rule with both
# wildcards in `chain forward` only, so despite the name it permits ping
# *through* the router and never makes the router itself answer. This is its
# input counterpart: src='*' with no dest lands in the top-level `chain input`,
# which fw4 renders ahead of every per-zone jump, so the router answers echo
# requests whatever a zone's input policy says -- zone_wan's REJECT, and the
# zone_lan DROP that omr-quota's block_lan installs, included. No family, so
# fw4 emits it for both ICMP echo-request and ICMPv6 echo-request; the WAN zone
# only ever accepted the IPv4 one.
if [ "$(uci -q get firewall.allowallpinginput)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		set firewall.allowallpinginput=rule
		set firewall.allowallpinginput.enabled='1'
		set firewall.allowallpinginput.name='Allow-All-Ping-Input'
		set firewall.allowallpinginput.target='ACCEPT'
		set firewall.allowallpinginput.proto='icmp'
		set firewall.allowallpinginput.src='*'
		set firewall.allowallpinginput.icmp_type='echo-request'
		set firewall.allowallpinginput.limit='1000/sec'
		commit firewall
	EOF
fi

if [ "$(uci -q show firewall | grep Allow-VPN-ICMP)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		add firewall rule
		set firewall.@rule[-1].enabled='1'
		set firewall.@rule[-1].target='ACCEPT'
		set firewall.@rule[-1].name='Allow-VPN-ICMP'
		set firewall.@rule[-1].proto='icmp'
		set firewall.@rule[-1].src='vpn'
		commit firewall
	EOF
fi
# FTP to the VPS's address, forwarded to the LAN: fw4 assigns no helper
# automatically in a zone that masquerades, as zone_vpn does, so passive FTP
# fails without this rule (openmptcprouter#4365). With it, the router
# rewrites the server's PASV reply to its tunnel address, which the VPS's own
# helper rewrites to the VPS's. Only where nf_conntrack_ftp is loaded: fw4
# would otherwise reference a helper it doesn't declare, and nft would reject
# the whole ruleset.
if [ "$(uci -q get firewall.ftphelpervpn)" = "" ] && [ -d /sys/module/nf_conntrack_ftp ]; then
	uci -q batch <<-EOF >/dev/null
		set firewall.ftphelpervpn=rule
		set firewall.ftphelpervpn.name='FTP-Helper-VPN'
		set firewall.ftphelpervpn.src='vpn'
		set firewall.ftphelpervpn.proto='tcp'
		set firewall.ftphelpervpn.dest_port='21'
		set firewall.ftphelpervpn.target='HELPER'
		set firewall.ftphelpervpn.set_helper='ftp'
		commit firewall
	EOF
fi
if [ "$(uci -q show firewall | grep Allow-Lan-to-Wan)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		add firewall rule
		set firewall.@rule[-1].enabled='1'
		set firewall.@rule[-1].target='ACCEPT'
		set firewall.@rule[-1].name='Allow-Lan-to-Wan'
		set firewall.@rule[-1].dest='wan'
		set firewall.@rule[-1].src='lan'
		set firewall.@rule[-1].proto='all'
		commit firewall
	EOF
fi

if [ "$(uci -q show firewall | grep ICMPv6-Lan-to-OMR)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		add firewall rule
		set firewall.@rule[-1].enabled='1'
		set firewall.@rule[-1].target='ACCEPT'
		set firewall.@rule[-1].name='ICMPv6-Lan-to-OMR'
		set firewall.@rule[-1].src='lan'
		set firewall.@rule[-1].family='ipv6'
		set firewall.@rule[-1].proto='icmp'
		set firewall.@rule[-1].limit='1000/sec'
		set firewall.@rule[-1].icmp_type='echo-reply destination-unreachable echo-request router-advertisement router-solicitation time-exceeded'
		commit firewall
	EOF
fi
#uci -q batch <<-EOF >/dev/null
#	del_list firewall.zone_wan.masq_dest='!10.0.0.0/8'
#	del_list firewall.zone_wan.masq_dest='!172.16.0.0/12'
#	del_list firewall.zone_wan.masq_dest='!192.168.0.0/16'
#	add_list firewall.zone_wan.masq_dest='!10.0.0.0/8'
#	add_list firewall.zone_wan.masq_dest='!172.16.0.0/12'
#	add_list firewall.zone_wan.masq_dest='!192.168.0.0/16'
#EOF
if [ "$(ubus call system board | jsonfilter -e '@.board_name')" = "bananapi,bpi-r2" ] || [ "$(ubus call system board | jsonfilter -e '@.board_name' | grep -i wrt)" != "" ]; then
	uci -q batch <<-EOF >/dev/null
	set firewall.@defaults[0].flow_offloading='1'
	set firewall.@defaults[0].flow_offloading_hw='1'
	EOF
fi

if [ "$(uci -q get firewall.omr_server)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		set firewall.omr_server=include
		set firewall.omr_server.path=/etc/firewall.omr-server
		commit firewall
	EOF
fi

if [ "$(uci -q get firewall.gre_tunnel)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		set firewall.gre_tunnel=include
		set firewall.gre_tunnel.path=/etc/firewall.gre-tunnel
		commit firewall
	EOF
fi
if [ "$(uci -q get firewall.ttl)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		set firewall.ttl=include
		set firewall.ttl.path=/etc/firewall.ttl
		set firewall.ttl.type='script'
		set firewall.ttl.fw4_compatible='1'
		commit firewall
	EOF
fi
if [ "$(uci -q get firewall.upnp)" = "" ] && [ -e /etc/init.d/miniupnpd ]; then
	uci -q batch <<-EOF >/dev/null
		set firewall.upnp=include
		set firewall.upnp.path='/etc/firewall.upnp'
		set firewall.upnp.type='script'
		set firewall.upnp.fw4_compatible='1'
		commit firewall
	EOF
fi
if [ "$(uci -q get firewall.fwlantovpn)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		set firewall.zone_lan.auto_helper='0'
		set firewall.fwlantovpn=forwarding
		set firewall.fwlantovpn.src='lan'
		set firewall.fwlantovpn.dest='vpn'
		commit firewall
	EOF
fi

if [ "$(uci -q get firewall.blockquicproxy)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		set firewall.blockquicproxy=rule
		set firewall.blockquicproxy.name='Block QUIC Proxy'
		set firewall.blockquicproxy.proto='udp'
		set firewall.blockquicproxy.dest_port='443'
		set firewall.blockquicproxy.target='DROP'
		set firewall.blockquicproxy.src='lan'
		set firewall.blockquicproxy.enabled='0'
		set firewall.blockquicall=rule
		set firewall.blockquicall.name='Block QUIC All'
		set firewall.blockquicall.proto='udp'
		set firewall.blockquicall.src='*'
		set firewall.blockquicall.dest='*'
		set firewall.blockquicall.dest_port='443'
		set firewall.blockquicall.target='DROP'
		set firewall.blockquicall.enabled='0'
		commit firewall
	EOF
fi

if [ "$(uci -q get firewall.allowicmpipv6)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		set firewall.allowicmpipv6=rule
		set firewall.allowicmpipv6.proto='icmp'
		set firewall.allowicmpipv6.target='ACCEPT'
		set firewall.allowicmpipv6.src='wan'
		set firewall.allowicmpipv6.name='Allow IPv6 ICMP'
		set firewall.allowicmpipv6.family='ipv6'
		set firewall.allowicmpipv6.limit='1000/sec'
		set firewall.allowicmpipv6.icmp_type='neighbour-advertisement neighbour-solicitation router-advertisement router-solicitation'
		commit firewall
	EOF
fi

if [ "$(uci -q get firewall.allowdhcpv6546)" = "" ]; then
	uci -q batch <<-EOF >/dev/null
		set firewall.allowdhcpv6546=rule
		set firewall.allowdhcpv6546.target='ACCEPT'
		set firewall.allowdhcpv6546.src='wan'
		set firewall.allowdhcpv6546.proto='udp'
		set firewall.allowdhcpv6546.dest_port='547'
		set firewall.allowdhcpv6546.name='Allow DHCPv6 (546-to-547)'
		set firewall.allowdhcpv6546.family='ipv6'
		set firewall.allowdhcpv6546.src_port='546'
		set firewall.allowdhcpv6547=rule
		set firewall.allowdhcpv6547.target='ACCEPT'
		set firewall.allowdhcpv6547.src='wan'
		set firewall.allowdhcpv6547.proto='udp'
		set firewall.allowdhcpv6547.dest_port='546'
		set firewall.allowdhcpv6547.name='Allow DHCPv6 (547-to-546)'
		set firewall.allowdhcpv6547.family='ipv6'
		set firewall.allowdhcpv6547.src_port='547'
		commit firewall
	EOF
fi

# Fix firewall config from some old config
allintf=$(uci -q get firewall.zone_wan.network)
uci -q del firewall.zone_wan.network
for intf in $allintf; do
	uci -q add_list firewall.zone_wan.network="${intf}"
done
allintf=$(uci -q get firewall.zone_vpn.network)
uci -q del firewall.zone_vpn.network
for intf in $allintf; do
	uci -q add_list firewall.zone_vpn.network="${intf}"
done


uci -q batch <<-EOF >/dev/null
	set firewall.zone_lan.mtu_fix='1'
	set firewall.zone_vpn.mtu_fix='1'
	commit firewall
EOF
if [ -z "$(uci -q get openmptcprouter.settings.sipalg)" ]; then
	uci -q batch <<-EOF >/dev/null
		set openmptcprouter.settings.sipalg='1'
		commit openmptcprouter
	EOF
fi
if [ -z "$(uci -q get openmptcprouter.settings.restrict_to_lan)" ]; then
	uci -q batch <<-EOF >/dev/null
		set openmptcprouter.settings.restrict_to_lan='1'
		commit openmptcprouter
	EOF
fi
if [ "$(uci -q get openmptcprouter.settings.sipalg)" = "0" ]; then
	uci -q batch <<-EOF >/dev/null
		set firewall.zone_lan.auto_helper='0'
		set firewall.zone_wan.auto_helper='0'
		set firewall.zone_vpn.auto_helper='0'
		commit firewall
	EOF
	[ -n "$(lsmod | grep nf_nat_sip)" ] && rmmod nf_nat_sip 2>&1 >/dev/null
	[ -n "$(lsmod | grep nf_conntrack_sip)" ] && rmmod nf_conntrack_sip 2>&1 >/dev/null
fi

# Remove mtu_fix check, doesn't work on kernel 6.6
sed -i 's/if (zone\.mtu_fix && this\.kernel < 0x040a0000) {/if (zone.mtu_fix \&\& this.kernel < 0x040a0000 \&\& false) {/' /usr/share/ucode/fw4.uc

uci -q batch <<-EOF >/dev/null
	del firewall.omr_server.reload
	del firewall.gre_tunnel.reload
	del firewall.ttl.reload
	commit firewall
EOF

if [ -z "$(uci -q get firewall.user)" ]; then
	uci -q batch <<-EOF >/dev/null
		set firewall.user=include
		set firewall.user.path='/etc/firewall.user'
		set firewall.user.enabled='1'
		set firewall.user.type='script'
		set firewall.user.fw4_compatible='1'
		commit firewall
	EOF
fi

# Rules saved from LuCI between luci-base 974b5864e0 and luci-app-firewall
# 08b7aed8 (v0.64, v0.64.1) lost their default action: a traffic rule left
# on "accept" has no target, which fw4 renders without a verdict, and a NAT
# rule left on "SNAT" has none either, so fw4 skips it (its own default,
# MASQUERADE, takes no rewrite address). LuCI shows them with those
# defaults, so write them. A rule with an option that LuCI only sets for
# another action (set_mark, set_xmark, set_dscp, set_helper), or never sets
# (log), is left alone.
. /lib/functions.sh
_omr_fw_default_target() {
	local section="$1" type="$2" target opt val
	config_get target "$section" target
	[ -n "$target" ] && return
	if [ "$type" = "rule" ]; then
		for opt in set_mark set_xmark set_dscp set_helper log; do
			config_get val "$section" "$opt"
			[ -n "$val" ] && return
		done
		uci -q set "firewall.${section}.target=ACCEPT"
	else
		config_get val "$section" snat_ip
		[ -z "$val" ] && config_get val "$section" snat_port
		[ -n "$val" ] && uci -q set "firewall.${section}.target=SNAT"
	fi
}
config_load firewall
config_foreach _omr_fw_default_target rule rule
config_foreach _omr_fw_default_target nat nat
[ -n "$(uci -q changes firewall)" ] && uci -q commit firewall

rm -f /tmp/luci-indexcache

exit 0
