Skip to content

Commit

Permalink
Added global lock to linux wg quick, fixed race condition when 2 wg i…
Browse files Browse the repository at this point in the history
…nterfaces 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.
  • Loading branch information
andrew-aladjev committed Apr 23, 2024
1 parent 13f4ac4 commit 5050316
Showing 1 changed file with 26 additions and 3 deletions.
29 changes: 26 additions & 3 deletions src/wg-quick/linux.bash
Original file line number Diff line number Diff line change
Expand Up @@ -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
"$@"
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Expand Down

0 comments on commit 5050316

Please sign in to comment.