[PATCH] added global lock to linux wg quick, fixed race condition when 2 wg interfaces wanted to have the same table number

Andrew Aladjev andrew.aladjev at protonmail.com
Tue Apr 23 08:12:25 UTC 2024


From: Andrew Aladjev <andrew.aladjev at gmail.com>

Signed-off-by: Andrew Aladjev <andrew.aladjev at gmail.com>
---
 src/wg-quick/linux.bash | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/src/wg-quick/linux.bash b/src/wg-quick/linux.bash
index 4193ce5..2a8fa16 100755
--- a/src/wg-quick/linux.bash
+++ b/src/wg-quick/linux.bash
@@ -27,6 +27,17 @@ CONFIG_FILE=""
 PROGRAM="${0##*/}"
 ARGS=( "$@" )
 
+LOCK_DIR_PATH="/var/lock"
+mkdir -p "$LOCK_DIR_PATH"
+
+WG_QUICK_LOCK_PATH="${LOCK_DIR_PATH}/wg-quick.lock"
+
+get_lock() {
+	exec {FD}>"$WG_QUICK_LOCK_PATH" || exit 1
+	flock -x "$FD" || exit 1
+	trap "rm -f $WG_QUICK_LOCK_PATH" EXIT
+}
+
 cmd() {
 	echo "[#] $*" >&2
 	"$@"
@@ -210,16 +221,22 @@ remove_firewall() {
 
 HAVE_SET_FIREWALL=0
 add_default() {
-	local table line
+	local proto=-4 iptables=iptables pf=ip
+	[[ $1 == *:* ]] && proto=-6 iptables=ip6tables pf=ip6
+
+	local table
 	if ! get_fwmark table; then
+		# We are trying to get next global table number and use it with default route.
+	        # This process should be globaly locked.
+	        get_lock
+
 		table=51820
 		while [[ -n $(ip -4 route show table $table 2>/dev/null) || -n $(ip -6 route show table $table 2>/dev/null) ]]; do
 			((table++))
 		done
 		cmd wg set "$INTERFACE" fwmark $table
 	fi
-	local proto=-4 iptables=iptables pf=ip
-	[[ $1 == *:* ]] && proto=-6 iptables=ip6tables pf=ip6
+
 	cmd ip $proto rule add not fwmark $table table $table
 	cmd ip $proto rule add table main suppress_prefixlength 0
 	cmd ip $proto route add "$1" dev "$INTERFACE" table $table
@@ -229,20 +246,26 @@ add_default() {
 	printf -v nftcmd '%sadd chain %s %s preraw { type filter hook prerouting priority -300; }\n' "$nftcmd" "$pf" "$nftable"
 	printf -v nftcmd '%sadd chain %s %s premangle { type filter hook prerouting priority -150; }\n' "$nftcmd" "$pf" "$nftable"
 	printf -v nftcmd '%sadd chain %s %s postmangle { type filter hook postrouting priority -150; }\n' "$nftcmd" "$pf" "$nftable"
+
+	local line
 	while read -r line; do
 		[[ $line =~ .*inet6?\ ([0-9a-f:.]+)/[0-9]+.* ]] || continue
 		printf -v restore '%s-I PREROUTING ! -i %s -d %s -m addrtype ! --src-type LOCAL -j DROP %s\n' "$restore" "$INTERFACE" "${BASH_REMATCH[1]}" "$marker"
 		printf -v nftcmd '%sadd rule %s %s preraw iifname != "%s" %s daddr %s fib saddr type != local drop\n' "$nftcmd" "$pf" "$nftable" "$INTERFACE" "$pf" "${BASH_REMATCH[1]}"
 	done < <(ip -o $proto addr show dev "$INTERFACE" 2>/dev/null)
+
 	printf -v restore '%sCOMMIT\n*mangle\n-I POSTROUTING -m mark --mark %d -p udp -j CONNMARK --save-mark %s\n-I PREROUTING -p udp -j CONNMARK --restore-mark %s\nCOMMIT\n' "$restore" $table "$marker" "$marker"
 	printf -v nftcmd '%sadd rule %s %s postmangle meta l4proto udp mark %d ct mark set mark \n' "$nftcmd" "$pf" "$nftable" $table
 	printf -v nftcmd '%sadd rule %s %s premangle meta l4proto udp meta mark set ct mark \n' "$nftcmd" "$pf" "$nftable"
+
 	[[ $proto == -4 ]] && cmd sysctl -q net.ipv4.conf.all.src_valid_mark=1
+
 	if type -p nft >/dev/null; then
 		cmd nft -f <(echo -n "$nftcmd")
 	else
 		echo -n "$restore" | cmd $iptables-restore -n
 	fi
+
 	HAVE_SET_FIREWALL=1
 	return 0
 }
-- 
2.40.1




More information about the WireGuard mailing list