initscripts: Always wait for xtables lock when running iptables commands
Commit Message
If not explicitly instructed to do so, iptables by default aborts with
an error message such as
> Can't lock /run/xtables.lock: Resource temporarily unavailable
> Another app is currently holding the xtables lock. Perhaps you want to use the -w option?
if the Xtables lock is still set, i.e., another iptables operation is
currently in progress. This causes iptables commands not to be executed
at all if there are delays during the boot procedure, e.g. due to slow
PPPoE dial-up procedure or similar.
To ensure deterministic behavior, this match modifies initscripts to
always execute iptables to wait for the Xtables lock to be removed, to
make sure iptables rules are installed properly (the "firewall"
initscript is doing so already).
Fixes: #13896 - OpenVPN RW port not opened in firewall after reboot
Signed-off-by: Peter Müller <peter.mueller@ipfire.org>
Tested-by: Peter Müller <peter.mueller@ipfire.org>
---
src/initscripts/networking/red | 4 ++--
src/initscripts/packages/tor | 10 +++++-----
src/initscripts/system/dhcp | 16 ++++++++--------
src/initscripts/system/openvpn-n2n | 10 +++++-----
src/initscripts/system/openvpn-rw | 6 +++---
src/initscripts/system/wireguard | 20 ++++++++++----------
6 files changed, 33 insertions(+), 33 deletions(-)
Comments
Hello Peter,
Nice to read you on this list again :)
I cannot say that I have ever noticed that we had any races here. I have merged the patch into next and it will be released with Core Update 199.
Best,
-Michael
> On 28 Sep 2025, at 20:51, Peter Müller <peter.mueller@ipfire.org> wrote:
>
> If not explicitly instructed to do so, iptables by default aborts with
> an error message such as
>
>> Can't lock /run/xtables.lock: Resource temporarily unavailable
>> Another app is currently holding the xtables lock. Perhaps you want to use the -w option?
>
> if the Xtables lock is still set, i.e., another iptables operation is
> currently in progress. This causes iptables commands not to be executed
> at all if there are delays during the boot procedure, e.g. due to slow
> PPPoE dial-up procedure or similar.
>
> To ensure deterministic behavior, this match modifies initscripts to
> always execute iptables to wait for the Xtables lock to be removed, to
> make sure iptables rules are installed properly (the "firewall"
> initscript is doing so already).
>
> Fixes: #13896 - OpenVPN RW port not opened in firewall after reboot
> Signed-off-by: Peter Müller <peter.mueller@ipfire.org>
> Tested-by: Peter Müller <peter.mueller@ipfire.org>
> ---
> src/initscripts/networking/red | 4 ++--
> src/initscripts/packages/tor | 10 +++++-----
> src/initscripts/system/dhcp | 16 ++++++++--------
> src/initscripts/system/openvpn-n2n | 10 +++++-----
> src/initscripts/system/openvpn-rw | 6 +++---
> src/initscripts/system/wireguard | 20 ++++++++++----------
> 6 files changed, 33 insertions(+), 33 deletions(-)
>
> diff --git a/src/initscripts/networking/red b/src/initscripts/networking/red
> index 6d779b365..536fc972c 100644
> --- a/src/initscripts/networking/red
> +++ b/src/initscripts/networking/red
> @@ -162,8 +162,8 @@ case "${1}" in
>
> elif [ "${TYPE}" == "DHCP" ]; then
> # Add firewall rules to allow comunication with the dhcp server on red.
> - iptables -A REDINPUT -p tcp --source-port 67 --destination-port 68 -i ${DEVICE} -j ACCEPT
> - iptables -A REDINPUT -p udp --source-port 67 --destination-port 68 -i ${DEVICE} -j ACCEPT
> + iptables --wait -A REDINPUT -p tcp --source-port 67 --destination-port 68 -i ${DEVICE} -j ACCEPT
> + iptables --wait -A REDINPUT -p udp --source-port 67 --destination-port 68 -i ${DEVICE} -j ACCEPT
>
> echo -n "${DEVICE}" > /var/ipfire/red/iface
>
> diff --git a/src/initscripts/packages/tor b/src/initscripts/packages/tor
> index 47797265c..eef9682f3 100644
> --- a/src/initscripts/packages/tor
> +++ b/src/initscripts/packages/tor
> @@ -37,19 +37,19 @@ function setup_firewall() {
> # Allow incoming traffic to Tor relay (and directory) port and
> # all outgoing TCP connections from Tor user.
> if [ "${TOR_RELAY_ENABLED}" = "on" -a -n "${TOR_RELAY_PORT}" ]; then
> - iptables -A TOR_INPUT -p tcp --dport "${TOR_RELAY_PORT}" -j ACCEPT
> - iptables -A TOR_OUTPUT -p tcp -m owner --uid-owner tor -j ACCEPT
> + iptables --wait -A TOR_INPUT -p tcp --dport "${TOR_RELAY_PORT}" -j ACCEPT
> + iptables --wait -A TOR_OUTPUT -p tcp -m owner --uid-owner tor -j ACCEPT
> fi
>
> if [ "${TOR_RELAY_ENABLED}" = "on" -a -n "${TOR_RELAY_DIRPORT}" ] && [ "${TOR_RELAY_DIRPORT}" -ne 0 ]; then
> - iptables -A TOR_INPUT -p tcp --dport "${TOR_RELAY_DIRPORT}" -j ACCEPT
> + iptables --wait -A TOR_INPUT -p tcp --dport "${TOR_RELAY_DIRPORT}" -j ACCEPT
> fi
> }
>
> function flush_firewall() {
> # Flush all rules.
> - iptables -F TOR_INPUT
> - iptables -F TOR_OUTPUT
> + iptables --wait -F TOR_INPUT
> + iptables --wait -F TOR_OUTPUT
> }
>
> case "${1}" in
> diff --git a/src/initscripts/system/dhcp b/src/initscripts/system/dhcp
> index 61b951658..826cd2dfe 100644
> --- a/src/initscripts/system/dhcp
> +++ b/src/initscripts/system/dhcp
> @@ -28,10 +28,10 @@ eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings)
> eval $(/usr/local/bin/readhash /var/ipfire/dhcp/settings)
>
> function flush_chains() {
> - iptables -F DHCPGREENINPUT
> - iptables -F DHCPGREENOUTPUT
> - iptables -F DHCPBLUEINPUT
> - iptables -F DHCPBLUEOUTPUT
> + iptables --wait -F DHCPGREENINPUT
> + iptables --wait -F DHCPGREENOUTPUT
> + iptables --wait -F DHCPBLUEINPUT
> + iptables --wait -F DHCPBLUEOUTPUT
> }
>
> case "$1" in
> @@ -41,14 +41,14 @@ case "$1" in
> if [ -n "${GREEN_DEV}" -a -e "/var/ipfire/dhcp/enable_green" ]; then
> LISTEN_INTERFACES+=" ${GREEN_DEV}"
>
> - iptables -A DHCPGREENINPUT -i "${GREEN_DEV}" -j DHCPINPUT
> - iptables -A DHCPGREENOUTPUT -o "${GREEN_DEV}" -j DHCPOUTPUT
> + iptables --wait -A DHCPGREENINPUT -i "${GREEN_DEV}" -j DHCPINPUT
> + iptables --wait -A DHCPGREENOUTPUT -o "${GREEN_DEV}" -j DHCPOUTPUT
> fi
> if [ -n "${BLUE_DEV}" -a -e "/var/ipfire/dhcp/enable_blue" ]; then
> LISTEN_INTERFACES+=" ${BLUE_DEV}"
>
> - iptables -A DHCPBLUEINPUT -i "${BLUE_DEV}" -j DHCPINPUT
> - iptables -A DHCPBLUEOUTPUT -o "${BLUE_DEV}" -j DHCPOUTPUT
> + iptables --wait -A DHCPBLUEINPUT -i "${BLUE_DEV}" -j DHCPINPUT
> + iptables --wait -A DHCPBLUEOUTPUT -o "${BLUE_DEV}" -j DHCPOUTPUT
> fi
>
> boot_mesg "Starting DHCP Server..."
> diff --git a/src/initscripts/system/openvpn-n2n b/src/initscripts/system/openvpn-n2n
> index 985398379..f6d554eaf 100644
> --- a/src/initscripts/system/openvpn-n2n
> +++ b/src/initscripts/system/openvpn-n2n
> @@ -63,10 +63,10 @@ update_firewall_rules() {
> local local_address
>
> # Flush the block chain
> - iptables -F OVPNBLOCK
> + iptables --wait -F OVPNBLOCK
>
> # Flush the NAT chain
> - iptables -t nat -F OVPNNAT
> + iptables --wait -t nat -F OVPNNAT
>
> local IFS=','
>
> @@ -85,10 +85,10 @@ update_firewall_rules() {
> fi
>
> # Open port
> - iptables -A OVPNINPUTN2N -p "${proto}" --dport "${port}" -j ACCEPT
> + iptables --wait -A OVPNINPUTN2N -p "${proto}" --dport "${port}" -j ACCEPT
>
> # Block all communication from transfer networks
> - iptables -A OVPNBLOCK -s "${transfer_subnet}" -j DROP
> + iptables --wait -A OVPNBLOCK -s "${transfer_subnet}" -j DROP
>
> # Calculate NAT addresses
> transfer_address="$(calculate_transfer_address "${transfer_subnet}" "${role}")"
> @@ -96,7 +96,7 @@ update_firewall_rules() {
>
> # NAT all outgoing connections away from the transfer net
> if [ -n "${transfer_address}" -a -n "${local_address}" ]; then
> - iptables -t nat -A OVPNNAT -s "${transfer_address}" \
> + iptables --wait -t nat -A OVPNNAT -s "${transfer_address}" \
> -j SNAT --to-source "${local_address}"
> fi
> done < /var/ipfire/ovpn/ovpnconfig
> diff --git a/src/initscripts/system/openvpn-rw b/src/initscripts/system/openvpn-rw
> index 6359d0d08..d506c8ebd 100644
> --- a/src/initscripts/system/openvpn-rw
> +++ b/src/initscripts/system/openvpn-rw
> @@ -38,10 +38,10 @@ case "${1}" in
> modprobe tun &>/dev/null
>
> # Flush all firewall rules
> - iptables -F OVPNINPUTRW
> + iptables --wait -F OVPNINPUTRW
>
> # Open the port
> - iptables -A OVPNINPUTRW \
> + iptables --wait -A OVPNINPUTRW \
> -p "${DPROTOCOL}" --dport "${DDEST_PORT}" -j ACCEPT
>
> boot_mesg "Starting OpenVPN Roadwarrior Server..."
> @@ -60,7 +60,7 @@ case "${1}" in
> killproc /usr/sbin/openvpn
>
> # Flush all firewall rules
> - iptables -F OVPNINPUTRW
> + iptables --wait -F OVPNINPUTRW
> ;;
>
> restart)
> diff --git a/src/initscripts/system/wireguard b/src/initscripts/system/wireguard
> index caaa69cb9..ead1cdce8 100644
> --- a/src/initscripts/system/wireguard
> +++ b/src/initscripts/system/wireguard
> @@ -216,7 +216,7 @@ generate_config() {
> ip addr add "${local_address}" dev "${intf}"
>
> # Apply MASQUERADE
> - iptables -t nat -A WGNAT -o "${intf}" -j MASQUERADE
> + iptables --wait -t nat -A WGNAT -o "${intf}" -j MASQUERADE
> fi
>
> echo "[Interface]"
> @@ -230,7 +230,7 @@ generate_config() {
> echo "ListenPort = ${port}"
>
> # Open the port
> - iptables -A WGINPUT -p udp --dport "${port}" -j ACCEPT
> + iptables --wait -A WGINPUT -p udp --dport "${port}" -j ACCEPT
> fi
>
> echo "[Peer]"
> @@ -285,7 +285,7 @@ generate_config() {
> # Set blocking rules
> for local_subnet in ${local_subnets//|/ }; do
> for remote_subnet in ${remote_subnets//|/ }; do
> - iptables -I WGBLOCK \
> + iptables --wait -I WGBLOCK \
> -s "${remote_subnet}" -d "${local_subnet}" -j RETURN
> done
> done
> @@ -297,23 +297,23 @@ generate_config() {
>
> reload_firewall() {
> # Flush all previous rules
> - iptables -F WGINPUT
> - iptables -t nat -F WGNAT
> + iptables --wait -F WGINPUT
> + iptables --wait -t nat -F WGNAT
>
> if [ "${ENABLED}" = "on" ]; then
> - iptables -A WGINPUT -p udp --dport "${PORT}" -j ACCEPT
> + iptables --wait -A WGINPUT -p udp --dport "${PORT}" -j ACCEPT
> fi
>
> - iptables -F WGBLOCK
> + iptables --wait -F WGBLOCK
>
> # Don't block any traffic from Roadwarrior peers
> if [ -n "${CLIENT_POOL}" ]; then
> - iptables -A WGBLOCK -s "${CLIENT_POOL}" -i wg0 -j RETURN
> - iptables -A WGBLOCK -d "${CLIENT_POOL}" -o wg0 -j RETURN
> + iptables --wait -A WGBLOCK -s "${CLIENT_POOL}" -i wg0 -j RETURN
> + iptables --wait -A WGBLOCK -d "${CLIENT_POOL}" -o wg0 -j RETURN
> fi
>
> # Block all other traffic
> - iptables -A WGBLOCK -j REJECT --reject-with icmp-admin-prohibited
> + iptables --wait -A WGBLOCK -j REJECT --reject-with icmp-admin-prohibited
>
> # Flush any custom routes
> ip route flush table wg 2>/dev/null
> --
> 2.51.0
>
@@ -162,8 +162,8 @@ case "${1}" in
elif [ "${TYPE}" == "DHCP" ]; then
# Add firewall rules to allow comunication with the dhcp server on red.
- iptables -A REDINPUT -p tcp --source-port 67 --destination-port 68 -i ${DEVICE} -j ACCEPT
- iptables -A REDINPUT -p udp --source-port 67 --destination-port 68 -i ${DEVICE} -j ACCEPT
+ iptables --wait -A REDINPUT -p tcp --source-port 67 --destination-port 68 -i ${DEVICE} -j ACCEPT
+ iptables --wait -A REDINPUT -p udp --source-port 67 --destination-port 68 -i ${DEVICE} -j ACCEPT
echo -n "${DEVICE}" > /var/ipfire/red/iface
@@ -37,19 +37,19 @@ function setup_firewall() {
# Allow incoming traffic to Tor relay (and directory) port and
# all outgoing TCP connections from Tor user.
if [ "${TOR_RELAY_ENABLED}" = "on" -a -n "${TOR_RELAY_PORT}" ]; then
- iptables -A TOR_INPUT -p tcp --dport "${TOR_RELAY_PORT}" -j ACCEPT
- iptables -A TOR_OUTPUT -p tcp -m owner --uid-owner tor -j ACCEPT
+ iptables --wait -A TOR_INPUT -p tcp --dport "${TOR_RELAY_PORT}" -j ACCEPT
+ iptables --wait -A TOR_OUTPUT -p tcp -m owner --uid-owner tor -j ACCEPT
fi
if [ "${TOR_RELAY_ENABLED}" = "on" -a -n "${TOR_RELAY_DIRPORT}" ] && [ "${TOR_RELAY_DIRPORT}" -ne 0 ]; then
- iptables -A TOR_INPUT -p tcp --dport "${TOR_RELAY_DIRPORT}" -j ACCEPT
+ iptables --wait -A TOR_INPUT -p tcp --dport "${TOR_RELAY_DIRPORT}" -j ACCEPT
fi
}
function flush_firewall() {
# Flush all rules.
- iptables -F TOR_INPUT
- iptables -F TOR_OUTPUT
+ iptables --wait -F TOR_INPUT
+ iptables --wait -F TOR_OUTPUT
}
case "${1}" in
@@ -28,10 +28,10 @@ eval $(/usr/local/bin/readhash /var/ipfire/ethernet/settings)
eval $(/usr/local/bin/readhash /var/ipfire/dhcp/settings)
function flush_chains() {
- iptables -F DHCPGREENINPUT
- iptables -F DHCPGREENOUTPUT
- iptables -F DHCPBLUEINPUT
- iptables -F DHCPBLUEOUTPUT
+ iptables --wait -F DHCPGREENINPUT
+ iptables --wait -F DHCPGREENOUTPUT
+ iptables --wait -F DHCPBLUEINPUT
+ iptables --wait -F DHCPBLUEOUTPUT
}
case "$1" in
@@ -41,14 +41,14 @@ case "$1" in
if [ -n "${GREEN_DEV}" -a -e "/var/ipfire/dhcp/enable_green" ]; then
LISTEN_INTERFACES+=" ${GREEN_DEV}"
- iptables -A DHCPGREENINPUT -i "${GREEN_DEV}" -j DHCPINPUT
- iptables -A DHCPGREENOUTPUT -o "${GREEN_DEV}" -j DHCPOUTPUT
+ iptables --wait -A DHCPGREENINPUT -i "${GREEN_DEV}" -j DHCPINPUT
+ iptables --wait -A DHCPGREENOUTPUT -o "${GREEN_DEV}" -j DHCPOUTPUT
fi
if [ -n "${BLUE_DEV}" -a -e "/var/ipfire/dhcp/enable_blue" ]; then
LISTEN_INTERFACES+=" ${BLUE_DEV}"
- iptables -A DHCPBLUEINPUT -i "${BLUE_DEV}" -j DHCPINPUT
- iptables -A DHCPBLUEOUTPUT -o "${BLUE_DEV}" -j DHCPOUTPUT
+ iptables --wait -A DHCPBLUEINPUT -i "${BLUE_DEV}" -j DHCPINPUT
+ iptables --wait -A DHCPBLUEOUTPUT -o "${BLUE_DEV}" -j DHCPOUTPUT
fi
boot_mesg "Starting DHCP Server..."
@@ -63,10 +63,10 @@ update_firewall_rules() {
local local_address
# Flush the block chain
- iptables -F OVPNBLOCK
+ iptables --wait -F OVPNBLOCK
# Flush the NAT chain
- iptables -t nat -F OVPNNAT
+ iptables --wait -t nat -F OVPNNAT
local IFS=','
@@ -85,10 +85,10 @@ update_firewall_rules() {
fi
# Open port
- iptables -A OVPNINPUTN2N -p "${proto}" --dport "${port}" -j ACCEPT
+ iptables --wait -A OVPNINPUTN2N -p "${proto}" --dport "${port}" -j ACCEPT
# Block all communication from transfer networks
- iptables -A OVPNBLOCK -s "${transfer_subnet}" -j DROP
+ iptables --wait -A OVPNBLOCK -s "${transfer_subnet}" -j DROP
# Calculate NAT addresses
transfer_address="$(calculate_transfer_address "${transfer_subnet}" "${role}")"
@@ -96,7 +96,7 @@ update_firewall_rules() {
# NAT all outgoing connections away from the transfer net
if [ -n "${transfer_address}" -a -n "${local_address}" ]; then
- iptables -t nat -A OVPNNAT -s "${transfer_address}" \
+ iptables --wait -t nat -A OVPNNAT -s "${transfer_address}" \
-j SNAT --to-source "${local_address}"
fi
done < /var/ipfire/ovpn/ovpnconfig
@@ -38,10 +38,10 @@ case "${1}" in
modprobe tun &>/dev/null
# Flush all firewall rules
- iptables -F OVPNINPUTRW
+ iptables --wait -F OVPNINPUTRW
# Open the port
- iptables -A OVPNINPUTRW \
+ iptables --wait -A OVPNINPUTRW \
-p "${DPROTOCOL}" --dport "${DDEST_PORT}" -j ACCEPT
boot_mesg "Starting OpenVPN Roadwarrior Server..."
@@ -60,7 +60,7 @@ case "${1}" in
killproc /usr/sbin/openvpn
# Flush all firewall rules
- iptables -F OVPNINPUTRW
+ iptables --wait -F OVPNINPUTRW
;;
restart)
@@ -216,7 +216,7 @@ generate_config() {
ip addr add "${local_address}" dev "${intf}"
# Apply MASQUERADE
- iptables -t nat -A WGNAT -o "${intf}" -j MASQUERADE
+ iptables --wait -t nat -A WGNAT -o "${intf}" -j MASQUERADE
fi
echo "[Interface]"
@@ -230,7 +230,7 @@ generate_config() {
echo "ListenPort = ${port}"
# Open the port
- iptables -A WGINPUT -p udp --dport "${port}" -j ACCEPT
+ iptables --wait -A WGINPUT -p udp --dport "${port}" -j ACCEPT
fi
echo "[Peer]"
@@ -285,7 +285,7 @@ generate_config() {
# Set blocking rules
for local_subnet in ${local_subnets//|/ }; do
for remote_subnet in ${remote_subnets//|/ }; do
- iptables -I WGBLOCK \
+ iptables --wait -I WGBLOCK \
-s "${remote_subnet}" -d "${local_subnet}" -j RETURN
done
done
@@ -297,23 +297,23 @@ generate_config() {
reload_firewall() {
# Flush all previous rules
- iptables -F WGINPUT
- iptables -t nat -F WGNAT
+ iptables --wait -F WGINPUT
+ iptables --wait -t nat -F WGNAT
if [ "${ENABLED}" = "on" ]; then
- iptables -A WGINPUT -p udp --dport "${PORT}" -j ACCEPT
+ iptables --wait -A WGINPUT -p udp --dport "${PORT}" -j ACCEPT
fi
- iptables -F WGBLOCK
+ iptables --wait -F WGBLOCK
# Don't block any traffic from Roadwarrior peers
if [ -n "${CLIENT_POOL}" ]; then
- iptables -A WGBLOCK -s "${CLIENT_POOL}" -i wg0 -j RETURN
- iptables -A WGBLOCK -d "${CLIENT_POOL}" -o wg0 -j RETURN
+ iptables --wait -A WGBLOCK -s "${CLIENT_POOL}" -i wg0 -j RETURN
+ iptables --wait -A WGBLOCK -d "${CLIENT_POOL}" -o wg0 -j RETURN
fi
# Block all other traffic
- iptables -A WGBLOCK -j REJECT --reject-with icmp-admin-prohibited
+ iptables --wait -A WGBLOCK -j REJECT --reject-with icmp-admin-prohibited
# Flush any custom routes
ip route flush table wg 2>/dev/null