--- wg-quick/freebsd.bash.orig	2025-12-12 22:28:42 UTC
+++ wg-quick/freebsd.bash
@@ -25,11 +25,18 @@ CONFIG_FILE=""
 POST_DOWN=( )
 SAVE_CONFIG=0
 CONFIG_FILE=""
+DESCRIPTION=""
+USERLAND=0
+MONITOR=1
+TRACK_DNS_CHANGES=0
 PROGRAM="${0##*/}"
 ARGS=( "$@" )
 
 IS_AWG_ON=0
 
+declare -A ROUTES
+declare -A ENDPOINTS_MAP
+
 cmd() {
 	echo "[#] $*" >&3
 	"$@"
@@ -40,7 +47,7 @@ die() {
 	exit 1
 }
 
-CONFIG_SEARCH_PATHS=( /etc/amnezia/amneziawg /usr/local/etc/amnezia/amneziawg )
+CONFIG_SEARCH_PATHS=( ${AWG_QUICK_CONFIG_SEARCH_PATHS:-%%ETCDIR%%} )
 
 unset ORIGINAL_TMPDIR
 make_temp() {
@@ -64,7 +71,7 @@ parse_options() {
 }
 
 parse_options() {
-	local interface_section=0 line key value stripped path v
+	local interface_section=0 line key value stripped path v last_public_key 
 	CONFIG_FILE="$1"
 	if [[ $CONFIG_FILE =~ ^[a-zA-Z0-9_=+.-]{1,15}$ ]]; then
 		for path in "${CONFIG_SEARCH_PATHS[@]}"; do
@@ -82,7 +89,7 @@ parse_options() {
 		stripped="${line%%\#*}"
 		key="${stripped%%=*}"; key="${key##*([[:space:]])}"; key="${key%%*([[:space:]])}"
 		value="${stripped#*=}"; value="${value##*([[:space:]])}"; value="${value%%*([[:space:]])}"
-		[[ $key == "["* ]] && interface_section=0
+		[[ $key == "["* ]] && interface_section=0 && last_public_key=""
 		[[ $key == "[Interface]" ]] && interface_section=1
 		if [[ $interface_section -eq 1 ]]; then
 			case "$key" in
@@ -96,9 +103,14 @@ parse_options() {
 			PreDown) PRE_DOWN+=( "$value" ); continue ;;
 			PostUp) POST_UP+=( "$value" ); continue ;;
 			PostDown) POST_DOWN+=( "$value" ); continue ;;
+			Description) DESCRIPTION="$value"; continue ;;
 			SaveConfig) read_bool SAVE_CONFIG "$value"; continue ;;
+			UserLand) read_bool USERLAND "$value"; continue ;;
+			Monitor) read_bool MONITOR "$value"; continue ;;
+			TrackDNSChanges) TRACK_DNS_CHANGES="$value"; continue ;;
 			esac
 			case "$key" in
+			
 			Jc);&
 			Jmin);&
 			Jmax);&
@@ -116,6 +128,17 @@ parse_options() {
 			I4);&
 			I5) IS_AWG_ON=1;;
 			esac
+		else
+			case "$key" in
+			PublicKey) last_public_key="$value" ;;
+			Routes) ROUTES["$last_public_key"]="$value"; continue ;;
+			Endpoint)
+				endpoint_host="${value%%:*}"
+				if ! [[ "$endpoint_host" =~ ^[0-9]+ ]]; then
+					ENDPOINTS_MAP["$last_public_key"]="$endpoint_host"
+				fi
+				;;
+			esac
 		fi
 		WG_CONFIG+="$line"$'\n'
 	done < "$CONFIG_FILE"
@@ -136,20 +159,24 @@ add_if() {
 
 add_if() {
 	local ret rc
-	local cmd="ifconfig wg create name "$INTERFACE""
-	if [[ $IS_AWG_ON == 1 ]]; then
-		cmd="amneziawg-go "$INTERFACE"";
-	fi
-	if ret="$(cmd $cmd 2>&1 >/dev/null)"; then
-		return 0
-	fi
-	rc=$?
-	if [[ $ret == *"ifconfig: ioctl SIOCSIFNAME (set name): File exists"* ]]; then
-		echo "$ret" >&3
-		return $rc
-	fi
-	echo "[!] Missing WireGuard kernel support ($ret). Falling back to slow userspace implementation." >&3
+	local cmd="ifconfig amn create name "$INTERFACE""
+	if [[ $USERLAND == 0 ]]; then
+        if [ -n "$DESCRIPTION" ]; then
+            ret="$(cmd $cmd description "$DESCRIPTION" 2>&1 >/dev/null)" && return 0
+        else
+            ret="$(cmd $cmd 2>&1 >/dev/null)" && return 0
+        fi
+        rc=$?
+        if [[ $ret == *"ifconfig: ioctl SIOCSIFNAME (set name): File exists"* ]]; then
+            echo "$ret" >&3
+            return $rc
+        fi
+    fi
+	echo "[!] Missing Amnezia kernel support ($ret). Falling back to slow userspace implementation." >&3
 	cmd "${WG_QUICK_USERSPACE_IMPLEMENTATION:-amneziawg-go}" "$INTERFACE"
+    if [ -n "$DESCRIPTION" ]; then
+        cmd ifconfig $INTERFACE description "$DESCRIPTION"
+    fi
 }
 
 del_routes() {
@@ -181,9 +208,9 @@ del_if() {
 	if [[ -S /var/run/amneziawg/$INTERFACE.sock ]]; then
 		cmd rm -f "/var/run/amneziawg/$INTERFACE.sock"
 	else
-		cmd ifconfig "$INTERFACE" destroy
+		cmd ifconfig -n "$INTERFACE" destroy
 	fi
-	while ifconfig "$INTERFACE" >/dev/null 2>&1; do
+	while ifconfig -n "$INTERFACE" >/dev/null 2>&1; do
 		# HACK: it would be nice to `route monitor` here and wait for RTM_IFANNOUNCE
 		# but it turns out that the announcement is made before the interface
 		# disappears so we sometimes get a hang. So, we're instead left with polling
@@ -193,21 +220,21 @@ up_if() {
 }
 
 up_if() {
-	cmd ifconfig "$INTERFACE" up
+	cmd ifconfig -n "$INTERFACE" up
 }
 
 add_addr() {
 	if [[ $1 == *:* ]]; then
-		cmd ifconfig "$INTERFACE" inet6 "$1" alias
+		cmd ifconfig -n "$INTERFACE" inet6 "$1" alias
 	else
-		cmd ifconfig "$INTERFACE" inet "$1" alias
+		cmd ifconfig -n "$INTERFACE" inet "$1" alias
 	fi
 }
 
 set_mtu() {
 	local mtu=0 endpoint output family
 	if [[ -n $MTU ]]; then
-		cmd ifconfig "$INTERFACE" mtu "$MTU"
+		cmd ifconfig -n "$INTERFACE" mtu "$MTU"
 		return
 	fi
 	while read -r _ endpoint; do
@@ -215,14 +242,16 @@ set_mtu() {
 		family=inet
 		[[ ${BASH_REMATCH[1]} == *:* ]] && family=inet6
 		output="$(route -n get "-$family" "${BASH_REMATCH[1]}" || true)"
-		[[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
-	done < <(wg show "$INTERFACE" endpoints)
+		[[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig -n "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
+	done < <(awg show "$INTERFACE" endpoints)
 	if [[ $mtu -eq 0 ]]; then
 		read -r output < <(route -n get default || true) || true
-		[[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
+		[[ $output =~ interface:\ ([^ ]+)$'\n' && $(ifconfig -n "${BASH_REMATCH[1]}") =~ mtu\ ([0-9]+) && ${BASH_REMATCH[1]} -gt $mtu ]] && mtu="${BASH_REMATCH[1]}"
 	fi
-	[[ $mtu -gt 0 ]] || mtu=1500
-	cmd ifconfig "$INTERFACE" mtu $(( mtu - 80 ))
+	if [[ $mtu -gt 0 && $mtu -lt 1420 ]]; then
+        # setup MTU only if discovered MTU is less then default
+    	cmd ifconfig -n "$INTERFACE" mtu $(( mtu - 80 )) || true
+	fi
 }
 
 
@@ -249,7 +278,7 @@ collect_endpoints() {
 	while read -r _ endpoint; do
 		[[ $endpoint =~ ^\[?([a-z0-9:.]+)\]?:[0-9]+$ ]] || continue
 		ENDPOINTS+=( "${BASH_REMATCH[1]}" )
-	done < <(wg show "$INTERFACE" endpoints)
+	done < <(awg show "$INTERFACE" endpoints)
 }
 
 set_endpoint_direct_route() {
@@ -304,25 +333,108 @@ monitor_daemon() {
 }
 
 monitor_daemon() {
-	echo "[+] Backgrounding route monitor" >&2
+	[[ $MONITOR -eq 0 ]] && return 0
+
+	if [[ $1 == "spawn" ]]; then
+		# re-run this function over daemon to detach from session
+	    echo "[+] Backgrounding route monitor" >&2
+        env TMPDIR="${ORIGINAL_TMPDIR}" daemon $0 monitor-daemon ${INTERFACE}
+        return 0
+    fi
+
 	(make_temp
 	trap 'del_routes; clean_temp; exit 0' INT TERM EXIT
 	exec >/dev/null 2>&1
-	exec 19< <(exec route -n monitor)
+	exec 19< <(exec stdbuf -oL route -n monitor)
 	local event pid=$!
 	# TODO: this should also check to see if the endpoint actually changes
 	# in response to incoming packets, and then call set_endpoint_direct_route
 	# then too. That function should be able to gracefully cleanup if the
 	# endpoints change.
 	while read -u 19 -r event; do
-		[[ $event == RTM_* ]] || continue
-		ifconfig "$INTERFACE" >/dev/null 2>&1 || break
+		ifconfig -n "$INTERFACE" >/dev/null 2>&1 || break
 		[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
 		# TODO: set the mtu as well, but only if up
 	done
 	kill $pid) & disown
 }
 
+wg_endpoints() {
+	awk '
+		BEGIN { RS=""; FS="\n" }
+		/Peer/ {
+		pk=""; ep=""
+		for (i = 1; i <= NF; i++) {
+			if ($i ~ /^PublicKey[ \t]*=/) {
+			pk = $i
+			sub(/^PublicKey[ \t]*=[ \t]*/, "", pk)
+			}
+			if ($i ~ /^Endpoint[ \t]*=/) {
+			ep = $i
+			sub(/^Endpoint[ \t]*=[ \t]*/, "", ep)
+			split(ep, parts, ":")
+			host = parts[1]
+			port = parts[2]
+			}
+		}
+		if (pk != "" && host != "" && port != "") {
+			print pk, host, port
+		}
+		}
+	'
+}
+
+tracker_pid_file() {
+	echo "/var/run/awg-quick.dns-tracker.${INTERFACE}.pid"
+}
+
+monitor_dns_changes() {
+	local pk peer_ip port peer_host host_ip
+
+	[[ $TRACK_DNS_CHANGES -eq 0 ]] && return 0
+
+	if [[ $1 == "spawn" ]]; then
+		# re-run this function over daemon to detach from session
+	    echo "[+] Backgrounding DNS tracker" >&2
+        env TMPDIR="${ORIGINAL_TMPDIR}" daemon $0 monitor-dns-changes ${INTERFACE}
+        return 0
+    fi
+
+	pid_file="$(tracker_pid_file)"
+	[[ -f "$pid_file" ]] && kill $(cat "$pid_file") 2>/dev/null || true
+
+	(
+		trap 'rm -f "$pid_file"; exit 0' INT TERM EXIT
+
+		set -e
+		while true; do
+			sleep $TRACK_DNS_CHANGES &
+			wait $!
+
+			$cmd awg showconf "$INTERFACE" 2> /dev/null | wg_endpoints | \
+			while read -r pk peer_ip port; do
+				peer_host="${ENDPOINTS_MAP[$pk]}"
+				if [[ -n "$peer_host" ]]; then
+					host_ip=$(host "$peer_host" 2>/dev/null | awk '/has address/ { print $4; exit; }') || continue
+
+					if [[ "$host_ip" = "$peer_ip" ]]; then
+						#echo "$pk matches ${peer_ip} <=> ${host_ip}"
+						:
+					else
+						logger -t awg-quick -p local0.notice \
+							"$INTERFACE/$pk host $peer_host:" \
+							"IP missmatch: $host_ip != $peer_ip, configuring endpoint" || true
+						$cmd awg set "$INTERFACE" peer "$pk" endpoint "$peer_host:$port" || true
+					fi
+				fi
+			done
+
+		done
+	) </dev/null >/dev/null 2>&1 3>&- & disown
+	echo "$!" > "$pid_file"
+}
+
+
 HAVE_SET_DNS=0
 set_dns() {
 	[[ ${#DNS[@]} -gt 0 ]] || return 0
@@ -361,7 +473,7 @@ set_config() {
 }
 
 set_config() {
-	echo "$WG_CONFIG" | cmd wg setconf "$INTERFACE" /dev/stdin
+	echo "$WG_CONFIG" | cmd awg setconf "$INTERFACE" /dev/stdin
 }
 
 save_config() {
@@ -393,7 +505,7 @@ save_config() {
 	done
 	old_umask="$(umask)"
 	umask 077
-	current_config="$(cmd wg showconf "$INTERFACE")"
+	current_config="$(cmd awg showconf "$INTERFACE")"
 	trap 'rm -f "$CONFIG_FILE.tmp"; clean_temp; exit' INT TERM EXIT
 	echo "${current_config/\[Interface\]$'\n'/$new_config}" > "$CONFIG_FILE.tmp" || die "Could not write configuration file"
 	sync "$CONFIG_FILE.tmp"
@@ -419,7 +531,7 @@ cmd_usage() {
 	  followed by \`.conf'. Otherwise, INTERFACE is an interface name, with
 	  configuration found at:
 	  ${CONFIG_SEARCH_PATHS[@]/%//INTERFACE.conf}.
-	  It is to be readable by wg(8)'s \`setconf' sub-command, with the exception
+	  It is to be readable by awg(8)'s \`setconf' sub-command, with the exception
 	  of the following additions to the [Interface] section, which are handled
 	  by $PROGRAM:
 
@@ -436,13 +548,27 @@ cmd_usage() {
 	  - SaveConfig: if set to \`true', the configuration is saved from the current
 	    state of the interface upon shutdown.
 
-	See wg-quick(8) for more info and examples.
+	See awg-quick(8) for more info and examples.
 	_EOF
 }
 
+get_routes() {
+	while read -r pub_key i; do
+		if [[ -v "ROUTES[$pub_key]" ]]; then
+			for route in ${ROUTES[$pub_key]//,/ }; do
+				echo "$route"
+			done
+		else
+			for j in $i; do 
+				[[ $j =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$j"
+			done
+		fi
+	done < <(awg show "$INTERFACE" allowed-ips) | sort -nr -k 2 -t /
+}
+
 cmd_up() {
 	local i
-	[[ -z $(ifconfig "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists"
+	[[ -z $(ifconfig -n "$INTERFACE" 2>/dev/null) ]] || die "\`$INTERFACE' already exists"
 	trap 'del_if; del_routes; clean_temp; exit' INT TERM EXIT
 	add_if
 	execute_hooks "${PRE_UP[@]}"
@@ -453,26 +579,31 @@ cmd_up() {
 	set_mtu
 	up_if
 	set_dns
-	for i in $(while read -r _ i; do for i in $i; do [[ $i =~ ^[0-9a-z:.]+/[0-9]+$ ]] && echo "$i"; done; done < <(wg show "$INTERFACE" allowed-ips) | sort -nr -k 2 -t /); do
+	for i in $(get_routes); do
 		add_route "$i"
 	done
 	[[ $AUTO_ROUTE4 -eq 1 || $AUTO_ROUTE6 -eq 1 ]] && set_endpoint_direct_route
-	monitor_daemon
+	monitor_daemon spawn
+	monitor_dns_changes spawn
 	execute_hooks "${POST_UP[@]}"
 	trap 'clean_temp; exit' INT TERM EXIT
 }
 
 cmd_down() {
-	[[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface"
+	[[ " $(awg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a Amnezia interface"
 	execute_hooks "${PRE_DOWN[@]}"
 	[[ $SAVE_CONFIG -eq 0 ]] || save_config
 	del_if
 	unset_dns
+	if [[ -f "$(tracker_pid_file)" ]]; then
+		kill $(cat "$(tracker_pid_file)") 2>/dev/null
+		rm -f "$(tracker_pid_file)"
+	fi
 	execute_hooks "${POST_DOWN[@]}"
 }
 
 cmd_save() {
-	[[ " $(wg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a WireGuard interface"
+	[[ " $(awg show interfaces) " == *" $INTERFACE "* ]] || die "\`$INTERFACE' is not a Amnezia interface"
 	save_config
 }
 
@@ -480,6 +611,10 @@ cmd_strip() {
 	echo "$WG_CONFIG"
 }
 
+cmd_reload() {
+  cmd awg setconf "$INTERFACE" <(cmd_strip)
+}
+
 # ~~ function override insertion point ~~
 
 make_temp
@@ -503,6 +638,18 @@ elif [[ $# -eq 2 && $1 == strip ]]; then
 	auto_su
 	parse_options "$2"
 	cmd_strip
+elif [[ $# -eq 2 && $1 == reload ]]; then
+	auto_su
+	parse_options "$2"
+	cmd_reload
+elif [[ $# -eq 2 && $1 == "monitor-daemon" ]]; then
+	auto_su
+	parse_options "$2"
+    monitor_daemon
+elif [[ $# -eq 2 && $1 == "monitor-dns-changes" ]]; then
+	auto_su
+	parse_options "$2"
+    monitor_dns_changes
 else
 	cmd_usage
 	exit 1
