#!/sbin/openrc-run
# Init script for ipset
# Copyright (C) 2012-2017 Kaarle Ritvanen
# Licensed under the terms of the GPL2

description="Manage IP sets in the Linux kernel"
description_save="Save firewall IP sets"
description_reload="Load firewall IP sets"

extra_started_commands="save reload"

IPSET=/usr/sbin/ipset
STATUS=0

if [ "${RC_SVCNAME#*.}" != "$RC_SVCNAME" ] ; then
	: ${netns:="${RC_SVCNAME#*.}"}
fi

# Add ip netns if configured to run in a netns namespace
if [ -n "$netns" ]; then
	test -e /run/netns/$netns || ip netns add $netns
	IPSET="ip netns exec $netns $IPSET"
	: ${ipset_def_dir:=/etc/ipset.$netns.d}
else
	: ${ipset_def_dir:=/etc/ipset.d}
fi

ipset() {
	$IPSET $* || STATUS=1
}

set_files() {
	(cd $ipset_def_dir && ls)
}

set_file() {
	grep -v ^# $ipset_def_dir/$1
}

set_exists() {
	$IPSET -n list $1 &> /dev/null
}

set_lists() {
	$IPSET save | sed "s/^create \\([^ ]\\+\\) list:set.*/\\1/;ta;d;:a"
}

sets() {
	$IPSET -n list
}


depend() {
	before iptables ip6tables
}

start() {
	reload
}

stop() {
	ebegin "Flushing firewall IP sets"

	for name in $(set_lists); do
		ipset destroy $name
	done

	for name in $(sets); do
		ipset destroy $name
	done

	eend $STATUS
}

save() {
	ebegin "Saving firewall IP sets"

	ipset save | while read cmd; do
		set -- $cmd
		local action=$1
		local file=$ipset_def_dir/$2
		shift 2
		if [ "$action" = create ]; then
			echo $* > $file
		elif [ "$action" = add ]; then
			echo $* >> $file
		fi
	done

	for name in $(set_files); do
		set_exists $name || rm -f $ipset_def_dir/$name
	done

	eend $STATUS
}

reload() {
	ebegin "Loading firewall IP sets"

	local swap=
	for name in $(set_files); do
		if set_exists $name; then
			swap="$swap $name"
		fi
	done

	for name in $(set_files); do
		local new=$name
		if set_exists $name; then
			new=_init_$name
		fi
		echo create $new $(set_file $name | head -n 1)
	done | ipset restore

	(
		for name in $(set_files); do
			local new=$name
			set_exists _init_$name && new=_init_$name
			set_file $name | sed "1d;s/^/add $new /"
		done

		for name in $swap; do
			echo swap $name _init_$name
		done
	) | ipset restore

	for name in $(set_lists); do
		[ -f $ipset_def_dir/$name ] || echo destroy $name
	done | ipset restore

	for name in $(sets); do
		[ -f $ipset_def_dir/$name ] || echo destroy $name
	done | ipset restore

	eend $STATUS
}
