From d1979a92689ccbe6b9e2669cbe6971fe7f8ec842 Mon Sep 17 00:00:00 2001 From: Andrew Aladjev Date: Tue, 23 Apr 2024 11:47:12 +0300 Subject: [PATCH] Added global lock to linux wg quick, fixed race condition when 2 wg interfaces wanted to have the same table number. 1. You are creating multiple wg interfaces and starting them in parallel. 2. All wg interfaces is trying to use table number 51820. 3. The first wg interface succeeds, other interfaces fail. What is the problem in wg quick? It is not globaly locking calculation of next table number. How to fix it? We need to add global flock. Signed-off-by: Andrew Aladjev --- 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 4193ce58..2a8fa16c 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 }