Blame SOURCES/ipset.start-stop

35a10e
#!/bin/sh
f5104d
#
f5104d
# ipset      Start and stop ipset firewall sets
f5104d
#
35a10e
# config: /etc/sysconfig/ipset-config
f5104d
35a10e
IPSET_BIN=/usr/sbin/ipset
f5104d
IPSET_CONFIG=/etc/sysconfig/ipset-config
35a10e
IPSET_DATA_COMPAT=/etc/sysconfig/ipset
35a10e
IPSET_DATA_COMPAT_BACKUP=${IPSET_DATA_COMPAT}.save
35a10e
IPSET_DATA_DIR=/etc/sysconfig/ipset.d
35a10e
IPSET_DATA_DIR_BACKUP=${IPSET_DATA_DIR}.save
35a10e
IPSET_DATA_SAVED_FLAG=${IPSET_DATA_DIR}/.saved
35a10e
IPSET_LOCK=/run/ipset.lock
35a10e
IPSET_RUN=/run/ipset.run
35a10e
CLEAN_FILES=""
f5104d
35a10e
trap "rm -rf \${CLEAN_FILES}" EXIT
f5104d
35a10e
[ -x ${IPSET_BIN} ] || { echo "ipset: Cannot execute ${IPSET_BIN}" >&2; exit 5; }
f5104d
35a10e
# Source ipset configuration
35a10e
[ -f ${IPSET_CONFIG} ] && . ${IPSET_CONFIG}
f5104d
35a10e
set -f
f5104d
35a10e
lock() {
35a10e
	CLEAN_FILES="${CLEAN_FILES} ${IPSET_LOCK}"
35a10e
	until mkdir ${IPSET_LOCK} 2>/dev/null; do :; done
f5104d
}
f5104d
35a10e
save() {
35a10e
	fail=0
35a10e
35a10e
	# Make backups of existing configuration first, if any
35a10e
	[ -d ${IPSET_DATA_DIR} ] && mv -Tf ${IPSET_DATA_DIR} ${IPSET_DATA_DIR_BACKUP}
35a10e
	[ -f ${IPSET_DATA_COMPAT} ] && mv -Tf ${IPSET_DATA_COMPAT} ${IPSET_DATA_COMPAT_BACKUP}
35a10e
35a10e
	rm -f ${IPSET_DATA_SAVED_FLAG}
35a10e
35a10e
	# Save each set in a separate file
35a10e
	mkdir -pm 700 ${IPSET_DATA_DIR}
35a10e
	IFS="
35a10e
"
35a10e
	for set in $(${IPSET_BIN} list -n -t); do
35a10e
		# Empty name allowed, use ".set" as suffix. 'ipset save' doesn't
35a10e
		# quote set names with spaces: if we have a space in the name,
35a10e
		# work around this by quoting it ourselves in the output.
35a10e
		if expr index "${set}" " " >/dev/null; then
35a10e
			:> "${IPSET_DATA_DIR}/${set}.set"
35a10e
			for line in $(${IPSET_BIN} save "${set}"); do
35a10e
				create=0
35a10e
				echo "${line}" | grep -q "^create " && create=1
35a10e
				if [ $create -eq 1 ]; then
35a10e
					line=${line#create *}
35a10e
				else
35a10e
					line=${line#add *}
35a10e
				fi
35a10e
				line=${line#${set} *}
35a10e
				set="$(echo ${set} | sed 's/"/\\"/'g)"
35a10e
				if [ $create -eq 1 ]; then
35a10e
					echo "create \"${set}\" ${line}" >> "${IPSET_DATA_DIR}/${set}.set"
35a10e
				else
35a10e
					echo "add \"${set}\" ${line}" >> "${IPSET_DATA_DIR}/${set}.set"
35a10e
				fi
35a10e
			done
35a10e
		else
35a10e
			${IPSET_BIN} save "${set}" > "${IPSET_DATA_DIR}/${set}.set" || fail=1
35a10e
		fi
35a10e
		[ -f "${IPSET_DATA_DIR}/${set}.set" ] && chmod 600 "${IPSET_DATA_DIR}/${set}.set"
35a10e
		[ $fail -eq 1 ] && echo "ipset: Cannot save set ${set}" >&2 && unset IFS && return 1
35a10e
	done
35a10e
	touch ${IPSET_DATA_SAVED_FLAG} || { unset IFS; return 1; }
35a10e
	unset IFS
f5104d
35a10e
	# Done: remove backups
35a10e
	rm -rf ${IPSET_DATA_DIR_BACKUP}
35a10e
	rm -rf ${IPSET_DATA_COMPAT_BACKUP}
f5104d
35a10e
	return 0
f5104d
}
f5104d
35a10e
load() {
35a10e
	if [ -f ${IPSET_DATA_SAVED_FLAG} ]; then
35a10e
		# If we have a cleanly saved directory with all sets, we can
35a10e
		# delete any left-overs and use it
35a10e
		rm -rf ${IPSET_DATA_DIR_BACKUP}
35a10e
		rm -f ${IPSET_DATA_COMPAT_BACKUP}
35a10e
	else
35a10e
		# If sets weren't cleanly saved, restore from backups
35a10e
		[ -d ${IPSET_DATA_DIR_BACKUP} ] && rm -rf ${IPSET_DATA_DIR} && mv -Tf ${IPSET_DATA_DIR_BACKUP} ${IPSET_DATA_DIR}
35a10e
		[ -f ${IPSET_DATA_COMPAT_BACKUP} ] && rm -f ${IPSET_DATA_COMPAT} && mv -Tf ${IPSET_DATA_COMPAT_BACKUP} ${IPSET_DATA_COMPAT}
35a10e
	fi
f5104d
35a10e
	if [ ! -d ${IPSET_DATA_DIR} -a ! -f ${IPSET_DATA_COMPAT} ]; then
35a10e
		echo "ipset: No existing configuration available, none loaded"
35a10e
		touch ${IPSET_RUN}
35a10e
		return
35a10e
	fi
f5104d
35a10e
	# Merge all sets into temporary file
35a10e
	merged="$(mktemp -q /tmp/ipset.XXXXXX)"
35a10e
	CLEAN_FILES="${CLEAN_FILES} ${merged}"
35a10e
	chmod 600 "${merged}"
35a10e
	set +f
35a10e
	if [ -d ${IPSET_DATA_DIR} ]; then
0190eb
		# Copy the first lines of each saved set first, as they create
0190eb
		# the sets, then the rest: list:set entries depend on other
0190eb
		# sets, so make sure they all get created first
35a10e
		for f in ${IPSET_DATA_DIR}/*; do
35a10e
			[ "${f}" = "${IPSET_DATA_DIR}/*" ] && break
0190eb
			head -n1 "${f}" >> ${merged}
0190eb
		done
0190eb
		for f in ${IPSET_DATA_DIR}/*; do
0190eb
			[ "${f}" = "${IPSET_DATA_DIR}/*" ] && break
0190eb
			tail -n +2 "${f}" >> ${merged}
35a10e
		done
35a10e
	fi
35a10e
	set -f
35a10e
	[ -f ${IPSET_DATA_COMPAT} ] && cat ${IPSET_DATA_COMPAT} >> ${merged}
35a10e
35a10e
	# Drop sets that aren't in saved data, mark conflicts with existing sets
35a10e
	conflicts=""
35a10e
	IFS="
35a10e
"
35a10e
	for set in $(${IPSET_BIN} list -n -t); do
35a10e
		grep -q "^create ${set} " ${merged} && conflicts="${conflicts}|${set}" && continue
35a10e
		${IPSET_BIN} destroy "${set}" 2>/dev/null
35a10e
		# We can't destroy the set if it's in use, flush it instead
35a10e
		[ $? -ne 0 ] && ${IPSET_BIN} flush "${set}"
35a10e
	done
35a10e
	unset IFS
35a10e
	conflicts="${conflicts#|*}"
35a10e
35a10e
	# Common case: if we have no conflicts, just restore in one shot
35a10e
	if [ -z "${conflicts}" ]; then
35a10e
		${IPSET_BIN} restore -! < ${merged}
35a10e
		[ $? -ne 0 ] && echo "ipset: Failed to restore configured sets" >&2
35a10e
		rm ${merged}
35a10e
		CLEAN_FILES="${CLEAN_FILES%* ${merged}}"
35a10e
		touch ${IPSET_RUN}
35a10e
		return
35a10e
	fi
f5104d
35a10e
	# Find a salt for md5sum that makes names of saved sets unique
35a10e
	salt=0
35a10e
	while true; do
35a10e
		unique=1
35a10e
		IFS="
35a10e
"
35a10e
		for set in $(${IPSET_BIN} list -n -t); do
35a10e
			grep -q "^create $(echo ${salt}${set} | md5sum | head -c31) " ${merged}
35a10e
			[ $? -eq 0 ] && unique=0 && break
35a10e
		done
35a10e
		unset IFS
35a10e
		[ ${unique} -eq 1 ] && break
35a10e
		salt=$((salt + 1))
35a10e
	done
35a10e
35a10e
	# Add sets, mangling names for conflicting sets
35a10e
	awk '/^(add|create) ('"${conflicts}"')/ { printf "%s ",$1; system("echo '${salt}'" $2 " | md5sum | head -c31"); $1=""; $2=""; print; next} {print}' ${merged} | ${IPSET_BIN} restore -!
35a10e
35a10e
	[ $? -ne 0 ] && echo "ipset: Failed to restore configured sets" >&2
35a10e
35a10e
	# Swap and delete old sets
35a10e
	IFS='|'
35a10e
	for set in ${conflicts}; do
35a10e
		mangled="$(echo ${salt}${set} | md5sum | head -c31)"
35a10e
		${IPSET_BIN} swap "${set}" "${mangled}" 2>/dev/null
35a10e
		if [ $? -ne 0 ]; then
35a10e
			# This fails if set types are different: try to destroy
35a10e
			# existing set
35a10e
			${IPSET_BIN} destroy "${set}" 2>/dev/null
35a10e
			if [ $? -ne 0 ]; then
35a10e
				# Conflicting set is in use, we can only warn
35a10e
				# and flush the existing set
35a10e
				echo "ipset: Cannot load set \"${set}\", set with same name and conflicting type in use" >&2
35a10e
				${IPSET_BIN} flush "${set}"
35a10e
				${IPSET_BIN} destroy "${mangled}"
35a10e
			else
35a10e
				${IPSET_BIN} rename "${mangled}" "${set}"
35a10e
			fi
35a10e
		else
35a10e
			${IPSET_BIN} destroy "${mangled}"
35a10e
		fi
35a10e
	done
35a10e
	unset IFS
f5104d
35a10e
	rm ${merged}
35a10e
	CLEAN_FILES="${CLEAN_FILES%* ${merged}}"
35a10e
	touch ${IPSET_RUN}
f5104d
}
f5104d
35a10e
cleanup() {
35a10e
	${IPSET_BIN} flush || echo "ipset: Failed to flush sets" >&2
35a10e
35a10e
	# Try to destroy all sets at once. This will fail if some are in use,
35a10e
	# destroy all the other ones in that case
35a10e
	${IPSET_BIN} destroy 2>/dev/null && return
35a10e
	IFS="
35a10e
"
35a10e
	for set in $(${IPSET_BIN} list -n -t); do
35a10e
		${IPSET_BIN} destroy "${set}" 2>/dev/null
35a10e
	done
35a10e
	unset IFS
f5104d
}
f5104d
f5104d
stop() {
35a10e
	[ -f ${IPSET_RUN} ] || { echo "ipset: not running"; return 0; }
35a10e
	[ "${IPSET_SAVE_ON_STOP}" = "yes" ] && { save || echo "ipset: Failed to save sets"; }
35a10e
35a10e
	# Nothing to stop if the ip_set module is not loaded
35a10e
	lsmod | grep -q "^ip_set " || { echo "ipset: not running"; rm ${IPSET_RUN}; return 0; }
35a10e
35a10e
	# If the xt_set module is in use, then iptables is using ipset, so
35a10e
	# refuse to stop the service
35a10e
	mod="$(lsmod | grep ^xt_set)"
35a10e
	if [ $? -eq 0 ]; then
35a10e
		if [ "$(echo ${mod} | tr -s ' ' | cut -d' ' -f3)" != "0" ]; then
35a10e
			echo "ipset: Current iptables configuration requires ipset" >&2 && return 1
f5104d
		fi
f5104d
	fi
f5104d
35a10e
	cleanup
f5104d
35a10e
	rm ${IPSET_RUN}
35a10e
	return 0
35a10e
}
f5104d
35a10e
lock
f5104d
case "$1" in
35a10e
start)
35a10e
	load
f5104d
	;;
35a10e
stop)
f5104d
	stop
f5104d
	;;
35a10e
reload)
35a10e
	cleanup
35a10e
	load
35a10e
	;;
35a10e
save)
35a10e
	save
f5104d
	;;
35a10e
*)
35a10e
	echo "Usage: $0 {start|stop|reload|save}" >&2
f5104d
	exit 1
f5104d
esac
f5104d
35a10e
exit $?