#!/sbin/openrc-run

description="A lightweight DNS, DHCP, RA, TFTP and PXE server"

extra_commands="checkconfig"
description_checkconfig="Check configuration syntax"

extra_started_commands="reload"
description_reload="Clear cache and reload hosts files"

# DNSMASQ_CONFFILE is here for backward compatibility (Alpine <3.16).
: ${cfgfile:=${DNSMASQ_CONFFILE:-"/etc/dnsmasq.conf"}}
: ${leasefile:="/var/lib/misc/$RC_SVCNAME.leases"}
: ${user:="dnsmasq"}
: ${group:="dnsmasq"}
: ${setup_bridge:="yes"}
: ${setup_command:=""}

command="/usr/sbin/dnsmasq"
# Tell dnsmasq to not create pidfile, that's responsibility of init system.
# DNSMASQ_OPTS is here for backward compatibility (Alpine <3.16).
command_args="--keep-in-foreground --pid-file= $DNSMASQ_OPTS $command_args --conf-file=$cfgfile"
command_background="yes"
pidfile="/run/$RC_SVCNAME.pid"

if [ "${RC_SVCNAME#*.}" != "$RC_SVCNAME" ] && yesno "$setup_bridge"; then
	BRIDGE="${RC_SVCNAME#*.}"
	: ${BRIDGE_ADDR:="10.0.3.1"}
	: ${BRIDGE_NETMASK:="255.255.255.0"}
	: ${BRIDGE_NETWORK:="10.0.3.0/24"}
	: ${BRIDGE_DHCP_RANGE:="10.0.3.2,10.0.3.254"}
	: ${BRIDGE_DHCP_MAX:="253"}
	: ${BRIDGE_MAC:="00:16:3e:00:00:00" }
	: ${DNSMASQ_LISTEN_BRIDGE_ADDR:=yes}
fi

depend() {
	provide dns
	need localmount net
	after bootmisc dbus
	use logger
}

setup_firewall() {
	local ins=$1 add=$2

	iptables -w $ins INPUT -i "$BRIDGE" -p udp --dport 67 -j ACCEPT
	iptables -w $ins INPUT -i "$BRIDGE" -p tcp --dport 67 -j ACCEPT
	iptables -w $ins INPUT -i "$BRIDGE" -p udp --dport 53 -j ACCEPT
	iptables -w $ins INPUT -i "$BRIDGE" -p tcp --dport 53 -j ACCEPT
	iptables -w $ins FORWARD -i "$BRIDGE" -j ACCEPT
	iptables -w $ins FORWARD -o "$BRIDGE" -j ACCEPT
	iptables -w -t nat $add POSTROUTING -s "$BRIDGE_NETWORK" ! -d "$BRIDGE_NETWORK" -j MASQUERADE
	iptables -w -t mangle $add POSTROUTING -o "$BRIDGE" -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill

	if yesno "$BRIDGE_IPV6_NAT" && [ -n "$BRIDGE_IPV6_NETWORK" ]; then
		ip6tables -w -t nat $add POSTROUTING -s "$BRIDGE_IPV6_NETWORK" ! -d "$BRIDGE_IPV6_NETWORK" -j MASQUERADE
	fi
}

setup_bridge() {
	einfo "Creating bridge $BRIDGE"

	if ! [ -d "/sys/class/net/$BRIDGE" ]; then
		ip link add dev "$BRIDGE" type bridge
	fi

	local addr
	ip link set dev "$BRIDGE" address "$BRIDGE_MAC" \
		&& for addr in $BRIDGE_ADDR $BRIDGE_ADDR_EXTRA; do
			case "$addr" in
				*/*) ip addr add "$addr" dev "$BRIDGE";;
				*) ip addr add "$addr/$BRIDGE_NETMASK" dev "$BRIDGE";;
			esac
		done \
		&& ip link set dev "$BRIDGE" up

	 echo 1 > /proc/sys/net/ipv4/ip_forward
	 echo 0 > "/proc/sys/net/ipv6/conf/$BRIDGE/accept_dad" || true

	if [ -n "$BRIDGE_IPV6_ADDR" ] && [ -n "$BRIDGE_IPV6_MASK" ] && [ "$BRIDGE_IPV6_NETWORK" ]; then
		echo 1 > /proc/sys/net/ipv6/conf/all/forwarding
		echo 0 > "/proc/sys/net/ipv6/conf/$BRIDGE/autoconf"

		ip -6 addr add dev "$BRIDGE" "$BRIDGE_IPV6_ADDR/$BRIDGE_IPV6_MASK"

		command_args="$command_args --dhcp-range=$BRIDGE_IPV6_ADDR,ra-only --listen-address $BRIDGE_IPV6_ADDR"
	fi

}

start_pre() {
	$command --test --conf-file="$cfgfile" >/dev/null 2>&1 \
		|| $command --test \
		|| return 1

	checkpath -m 0644 -o "$user:$group" -f "$leasefile" || return 1

	if [ -n "$BRIDGE" ]; then
		setup_bridge
		if ! yesno "$DISABLE_IPTABLES"; then
			setup_firewall -I -A
		fi
		if yesno "$DNSMASQ_LISTEN_BRIDGE_ADDR"; then
			local addr; for addr in $BRIDGE_ADDR; do
				command_args="$command_args --listen-address ${addr%/*}"
			done
		fi
		command_args="$command_args --strict-order --bind-interfaces --except-interface=lo --interface=$BRIDGE"
		command_args="$command_args --dhcp-range $BRIDGE_DHCP_RANGE --dhcp-lease-max=$BRIDGE_DHCP_MAX --dhcp-no-override --dhcp-leasefile=$leasefile --dhcp-authoritative"
	fi

	if command -v "$setup_command" >/dev/null; then
		$setup_command || return 1
	fi
}

stop_post() {
	if [ -n "$BRIDGE" ]; then
		local addr; for addr in $BRIDGE_ADDR $BRIDGE_ADDR_EXTRA; do
			case "$addr" in
				*/*) ip addr del "$addr" dev "$BRIDGE";;
				*) ip addr del "$addr/$BRIDGE_NETMASK" dev "$BRIDGE";;
			esac
		done
		ip link set dev "$BRIDGE" down
		if ! yesno "$DISABLE_IPTABLES"; then
			setup_firewall -D -D
		fi
		# dont destroy if there are attached interfaces
		ls /sys/class/net/"$BRIDGE"/brif/* > /dev/null 2>&1 || ip link delete "$BRIDGE"
	fi
}

reload() {
	ebegin "Reloading $RC_SVCNAME"

	$command --test --conf-file="$cfgfile" >/dev/null 2>&1 \
		|| $command --test \
		|| return 1

	if [ "$supervisor" ]; then
		$supervisor "$RC_SVCNAME" --signal HUP
	else
		start-stop-daemon --signal HUP --pidfile "$pidfile"
	fi
	eend $?
}

checkconfig() {
	ebegin "Checking $RC_SVCNAME configuration"

	$command --test --conf-file="$cfgfile"

	eend $?
}
