blob: 7df800efc1d2d77b0cd34b5bd3d8e09f71f96ff9 [file] [log] [blame] [raw]
#!/bin/sh
# Copyright 2015-2025 Rivoreo
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
# IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
PROGRAM_DIR="${0%/*}"
[ "$PROGRAM_DIR" = "$0" ] && PROGRAM_DIR="$PWD"
set -e
ports_list_file="`mktemp`"
trap 'rm -f "$ports_list_file"' EXIT
i=8192
while [ $i -lt 65535 ]; do printf %04X\\n $i; i=$((i+1)); done > "$ports_list_file"
print_free_udp_port() {
sed -En -e 1d -e 's/^ *[0-9]+: [0-9A-F]{8}:([0-9A-F]{4}) .+/\1/p' /proc/net/udp | grep --fixed-strings --line-regexp --invert-match --file - "$ports_list_file" | sort --random-sort | { read n && echo $((0x$n)); }
}
parse_key_value() {
case "$1" in
*=*)
key="${1%%=*}"
key="${key%%[ ]*}"
value="${1#*=}"
local trim_value
while trim_value="${value#[ ]}" && [ "$trim_value" != "$value" ]; do value="$trim_value"; done
;;
*\ *|*\ *)
key="${1%%[ ]*}"
value="${1#*[ ]}"
;;
*)
false
;;
esac
}
print_peer_in_tunnel_address() {
local ORIG_IFS="$IFS"
IFS=.
set -- $1
IFS="$ORIG_IFS"
[ $# = 4 ] || return
printf "%s.%s.%s." $1 $2 $3
echo $(($4+($4%2?-1:1)))
}
rand() {
eval "$1=\"\`dd if=/dev/urandom count=1 bs=4 2> /dev/null | cksum\`\"" || return
eval "[ -n \"\$$1\" ]" || return
eval "$1=\${$1%% *}"
}
random_sleep() {
local n
rand n || return
sleep $((n%($2-$1)+$1)) || true
}
set -f
while true; do
if ! interfaces="`wg show interfaces`"; then
sleep 240
continue
fi
set -- $interfaces
min_sleep=$((3600/$#))
[ $min_sleep -lt 30 ] && min_sleep=30
max_sleep=$((115200/$#))
[ $max_sleep -lt 810 ] && max_sleep=810
have_matched_interface=
for i in $@; do
[ -f "/sys/class/net/$i/flags" ] || continue
[ -f "/etc/wireguard/$i.conf" ] || continue
flags="`cat \"/sys/class/net/$i/flags\"`" || continue
[ $((flags&1)) = 1 ] || continue
section=
my_private_key=
port=
dyn_port=
remote_peer_name=
in_tunnel_ip_addr=
peer_ssh_port=22
while read -r line; do case "$line" in
\[[Ii][Nn][Tt][Ee][Rr][Ff][Aa][Cc][Ee]\]*)
section=interface
;;
\[*\]*)
section=
;;
?*=*)
[ "$section" != interface ] && continue
parse_key_value "$line"
case "$key" in
[Ll][Ii][Ss][Tt][Ee][Nn][Pp][Oo][Rr][Tt])
port="$value"
;;
[Pp][Rr][Ii][Vv][Aa][Tt][Ee][Kk][Ee][Yy])
my_private_key="$value"
;;
"#RNCN#DynamicBindPort")
[ "$value" = true ] && dyn_port=1
;;
"#RNCN#RemotePeerName")
remote_peer_name="$value"
;;
[Aa][Dd][Dd][Rr][Ee][Ss][Ss])
addr_without_prefixlen="${value%/31}"
[ "$addr_without_prefixlen" = "$value" ] && continue
in_tunnel_ip_addr="`print_peer_in_tunnel_address \"$addr_without_prefixlen\"`"
;;
"#RNCN#PeerSSHPort")
peer_ssh_port="$value"
;;
esac
;;
esac done < "/etc/wireguard/$i.conf"
[ -n "$port" ] && [ -z "$dyn_port" ] && continue
random_sleep 2 480
if
if [ -n "$dyn_port" ]; then
[ -z "$my_private_key" ] && continue
[ -z "$remote_peer_name" ] && continue
[ -z "$in_tunnel_ip_addr" ] && continue
my_public_key="`printf '%s\\n' \"$my_private_key\" | wg pubkey`"
new_port="`print_free_udp_port`"
ssh "$in_tunnel_ip_addr" -p "$peer_ssh_port" \
-l wgcfg -i "$PROGRAM_DIR/../config/id_ecdsa_wgcfg" \
"rncn set-remote-port $remote_peer_name $my_public_key $new_port" \
< /dev/null || continue
wg set "$i" listen-port "$new_port"
else
wg set "$i" listen-port 0
fi
then
have_matched_interface=1
random_sleep $min_sleep $max_sleep
fi
done
[ -z "$have_matched_interface" ] && sleep 120
done