YazFi.sh
#!/bin/sh

#############################################
##                                         ##
##     __     __          ______  _        ##
##     \ \   / /         |  ____|(_)       ##
##      \ \_/ /__ _  ____| |__    _        ##
##       \   // _  ||_  /|  __|  | |       ##
##        | || (_| | / / | |     | |       ##
##        |_| \__,_|/___||_|     |_|       ##
##                                         ##
##    https://github.com/jackyaz/YazFi/    ##
##                                         ##
#############################################
##   Credit to @RMerlin for the original   ##
##    guest network DHCP script and for    ##
##         AsusWRT-Merlin firmware         ##
#############################################
# Last Modified: 2023-Nov-18
#--------------------------------------------------

######       Shellcheck directives     ######
# shellcheck disable=SC1003
# shellcheck disable=SC1090
# shellcheck disable=SC2005
# shellcheck disable=SC2016
# shellcheck disable=SC2018
# shellcheck disable=SC2019
# shellcheck disable=SC2034
# shellcheck disable=SC2039
# shellcheck disable=SC2059
# shellcheck disable=SC2086
# shellcheck disable=SC2140
# shellcheck disable=SC2155
# shellcheck disable=SC3003
# shellcheck disable=SC3043
#############################################

### Start of script variables ###
readonly SCRIPT_NAME="YazFi"
readonly SCRIPT_CONF="/jffs/addons/$SCRIPT_NAME.d/config"
readonly YAZFI_VERSION="v4.4.4"
readonly SCRIPT_VERSION="v4.4.4"
SCRIPT_BRANCH="master"
SCRIPT_REPO="https://jackyaz.io/$SCRIPT_NAME/$SCRIPT_BRANCH"
readonly SCRIPT_DIR="/jffs/addons/$SCRIPT_NAME.d"
readonly USER_SCRIPT_DIR="$SCRIPT_DIR/userscripts.d"
readonly SCRIPT_WEBPAGE_DIR="$(readlink /www/user)"
readonly SCRIPT_WEB_DIR="$SCRIPT_WEBPAGE_DIR/$SCRIPT_NAME"
readonly SHARED_DIR="/jffs/addons/shared-jy"
readonly SHARED_REPO="https://jackyaz.io/shared-jy/master"
readonly SHARED_WEB_DIR="$SCRIPT_WEBPAGE_DIR/shared-jy"
### End of script variables ###

### Start of output format variables ###
readonly CRIT="\\e[41m"
readonly ERR="\\e[31m"
readonly WARN="\\e[33m"
readonly PASS="\\e[32m"
readonly BOLD="\\e[1m"
readonly SETTING="${BOLD}\\e[36m"
readonly CLEARFORMAT="\\e[0m"
### End of output format variables ###

### Start of router environment variables ###
readonly LAN="$(nvram get lan_ipaddr)"
[ -z "$(nvram get odmpid)" ] && ROUTER_MODEL=$(nvram get productid) || ROUTER_MODEL=$(nvram get odmpid)

##-------------------------------------##
## Added by Martinski W. [2022-Nov-18] ##
##-------------------------------------##
_GetWiFiVirtualInterfaceNames_()
{
   local IFListStr=""
   for IFname in $(nvram get wl0_vifnames) $(nvram get wl1_vifnames) $(nvram get wl2_vifnames) $(nvram get wl3_vifnames)
   do
       IFname="$(echo "$IFname" | grep -E "^wl[0-3][.][1-3]$")"
       [ -n "$IFname" ] && { [ -n "$IFListStr" ] && IFListStr="$IFListStr $IFname" || IFListStr="$IFname" ; }
   done
   echo "$IFListStr"
}

##----------------------------------------##
## Modified by Martinski W. [2022-Nov-18] ##
##----------------------------------------##
readonly IFACELIST_FULL="wl0.1 wl0.2 wl0.3 wl1.1 wl1.2 wl1.3 wl2.1 wl2.2 wl2.3 wl3.1 wl3.2 wl3.3"
IFACELIST="$(_GetWiFiVirtualInterfaceNames_)"
### End of router environment variables ###

### Start of path variables ###
readonly DNSCONF="$SCRIPT_DIR/.dnsmasq"
readonly TMPCONF="$SCRIPT_DIR/.dnsmasq.tmp"
### End of path variables ###

### Start of firewall variables ###
readonly INPT="${SCRIPT_NAME}INPUT"
readonly FWRD="${SCRIPT_NAME}FORWARD"
readonly LGRJT="${SCRIPT_NAME}REJECT"
readonly DNSFLTR="${SCRIPT_NAME}DNSFILTER"
readonly DNSFLTR_DOT="${SCRIPT_NAME}DNSFILTER_DOT"
readonly CHAINS="$INPT $FWRD $LGRJT $DNSFLTR_DOT"
readonly NATCHAINS="$DNSFLTR"
### End of firewall variables ###

### Start of VPN clientlist variables ###
VPN_IP_LIST_ORIG_1=""
VPN_IP_LIST_ORIG_2=""
VPN_IP_LIST_ORIG_3=""
VPN_IP_LIST_ORIG_4=""
VPN_IP_LIST_ORIG_5=""
VPN_IP_LIST_NEW_1=""
VPN_IP_LIST_NEW_2=""
VPN_IP_LIST_NEW_3=""
VPN_IP_LIST_NEW_4=""
VPN_IP_LIST_NEW_5=""
### End of VPN clientlist variables ###

##----------------------------------------------##
## Added/modified by Martinski W. [2023-Jan-28] ##
##----------------------------------------------##
# DHCP Lease Time: Min & Max Values in seconds.
# 2 minutes=120 to 90 days=7776000 (inclusive).
# Single '0' or 'I' indicates "infinite" value.
#------------------------------------------------#
readonly MinDHCPLeaseTime=120
readonly MaxDHCPLeaseTime=7776000
readonly InfiniteLeaseTimeTag="I"
readonly InfiniteLeaseTimeVal="infinite"

##-------------------------------------##
## Added by Martinski W. [2022-Dec-23] ##
##-------------------------------------##
readonly SUPPORTstr="$(nvram get rc_support)"

if echo "$SUPPORTstr" | grep -qw '2.4G'
then Band_24G_Support=true
else Band_24G_Support=false
fi

if echo "$SUPPORTstr" | grep -qw '5G'
then Band_5G_1_Support=true
else Band_5G_1_Support=false
fi

if echo "$SUPPORTstr" | grep -qw '5G-2'
then Band_5G_2_support=true
else Band_5G_2_support=false
fi

if echo "$SUPPORTstr" | grep -qw 'wifi6e'
then Band_6G_1_Support=true
else Band_6G_1_Support=false
fi

# $1 = print to syslog, $2 = message to print, $3 = log level
Print_Output(){
	if [ "$1" = "true" ]; then
		logger -t "$SCRIPT_NAME" "$2"
	fi
	printf "${BOLD}${3}%s${CLEARFORMAT}\\n\\n" "$2"
}

Generate_Random_String(){
	PASSLENGTH=16
	if Validate_Number "" "$1" silent; then
		if [ "$1" -le 32 ] && [ "$1" -ge 8 ]; then
			PASSLENGTH="$1"
		else
			printf "${BOLD}Number is not between 8 and 32, using default of 16 characters${CLEARFORMAT}\\n"
		fi
	else
		printf "${BOLD}Invalid number provided, using default of 16 characters${CLEARFORMAT}\\n"
	fi

	< /dev/urandom tr -cd 'A-Za-z0-9' | head -c "$PASSLENGTH"
}

Escape_Sed(){
	sed -e 's/</\\</g;s/>/\\>/g;s/ /\\ /g'
}

Get_Iface_Var(){
	echo "$1" | sed -e 's/\.//g'
}

##-------------------------------------##
## Added by Martinski W. [2022-Dec-23] ##
##-------------------------------------##
GetIFaceUILabel()
{
	if [ $# -eq 0 ] || [ -z "$1" ] ; then echo "" ; fi

	theUILabel="*UNKNOWN*"
	case "$1" in
		wl0)
		    if [ "$ROUTER_MODEL" = "GT-AXE16000" ] && "$Band_5G_1_Support"
		    then theUILabel="5GHz"
		    elif "$Band_24G_Support" ; then theUILabel="2.4GHz"
		    fi
		    ;;
		wl1)
		    if [ "$ROUTER_MODEL" = "GT-AXE16000" ] && "$Band_5G_2_support"
		    then theUILabel="5GHz2"
		    elif "$Band_5G_1_Support" ; then theUILabel="5GHz"
		    fi
		    ;;
		wl2)
		    if [ "$ROUTER_MODEL" = "GT-AXE16000" ] || "$Band_6G_1_Support"
		    then theUILabel="6GHz"
		    elif "$Band_5G_2_support" ; then theUILabel="5GHz2"
		    fi
		    ;;
		wl3)
		    if [ "$ROUTER_MODEL" = "GT-AXE16000" ] && "$Band_24G_Support"
		    then theUILabel="2.4GHz"
		    fi
		    ;;
	esac
	echo "$theUILabel"
}

##----------------------------------------##
## Modified by Martinski W. [2022-Dec-23] ##
##----------------------------------------##
Get_Guest_Name()
{
	if [ $# -eq 0 ] || [ -z "$1" ] ; then echo "" ; fi
	theIFprefix="$(echo "$1" | cut -d '.' -f1)"
	theIFnumber="$(echo "$1" | cut -d '.' -f2)"
	theIFlabel="$(GetIFaceUILabel "$theIFprefix")"

	echo "YazFi $theIFlabel $theIFnumber"
}

##----------------------------------------##
## Modified by Martinski W. [2022-Dec-23] ##
##----------------------------------------##
Get_Guest_Name_Old()
{
	if [ $# -eq 0 ] || [ -z "$1" ] ; then echo "" ; fi
	theIFprefix="$(echo "$1" | cut -d '.' -f1)"
	theIFnumber="$(echo "$1" | cut -d '.' -f2)"
	theIFlabel="$(GetIFaceUILabel "$theIFprefix")"

	if [ "$theIFlabel" = "5GHz" ]
	then theIFlabel="5GHz1" ; fi

	echo "$theIFlabel Guest $theIFnumber"
}

Set_WiFi_Passphrase(){
	nvram set "${1}_wpa_psk"="$2"
	nvram set "${1}_auth_mode_x"="psk2"
	nvram set "${1}_akm"="psk2"
	nvram commit
}

Iface_Manage(){
	case $1 in
		create)
			ifconfig "$2" "$(eval echo '$'"$(Get_Iface_Var "$2")"_IPADDR | cut -f1-3 -d".").$(nvram get lan_ipaddr | cut -f4 -d".")" netmask 255.255.255.0
		;;
		delete)
			ifconfig "$2" 0.0.0.0
		;;
		deleteall)
			for IFACE in $IFACELIST; do
				Iface_Manage delete "$IFACE"
			done
		;;
	esac
}

Iface_BounceClients(){
	Print_Output true "Forcing $SCRIPT_NAME Guest WiFi clients to reauthenticate" "$PASS"

	for IFACE in $IFACELIST; do
		wl -i "$IFACE" radio off >/dev/null 2>&1
	done

	sleep 10

	for IFACE in $IFACELIST; do
		wl -i "$IFACE" radio on >/dev/null 2>&1
	done

	ARPDUMP="$(arp -an)"
	for IFACE in $IFACELIST; do
		if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_ENABLED")" = "true" ]; then
			IFACE_MACS="$(wl -i "$IFACE" assoclist)"
			if [ "$IFACE_MACS" != "" ]; then
				IFS=$'\n'
				for GUEST_MAC in $IFACE_MACS; do
					GUEST_MACADDR="${GUEST_MAC#* }"
					GUEST_ARPINFO="$(arp -an | grep -i "$GUEST_MACADDR")"
					for ARP_ENTRY in $GUEST_ARPINFO; do
						GUEST_IPADDR="$(echo "$GUEST_ARPINFO" | awk '{print $2}' | sed -e 's/(//g;s/)//g')"
						arp -d "$GUEST_IPADDR"
					done
				done
				unset IFS
			fi
		fi
	done

	ip -s -s neigh flush all >/dev/null 2>&1
	killall -q networkmap
	sleep 5
	if [ -z "$(pidof networkmap)" ]; then
		networkmap >/dev/null 2>&1 &
	fi
}

Auto_DNSMASQ(){
	case $1 in
		create)
			if [ -f /jffs/scripts/dnsmasq.postconf ]; then
				STARTUPLINECOUNT=$(grep -c "# $SCRIPT_NAME" /jffs/scripts/dnsmasq.postconf)
				STARTUPLINECOUNTEX=$(grep -cx "cat $DNSCONF >> /etc/dnsmasq.conf # $SCRIPT_NAME" /jffs/scripts/dnsmasq.postconf)

				if [ "$STARTUPLINECOUNT" -gt 1 ] || { [ "$STARTUPLINECOUNTEX" -eq 0 ] && [ "$STARTUPLINECOUNT" -gt 0 ]; }; then
					sed -i -e '/# '"$SCRIPT_NAME"'/d' /jffs/scripts/dnsmasq.postconf
				fi

				if [ "$STARTUPLINECOUNTEX" -eq 0 ]; then
					echo "cat $DNSCONF >> /etc/dnsmasq.conf # $SCRIPT_NAME" >> /jffs/scripts/dnsmasq.postconf
				fi

				if [ "$(grep -c "NextDNS" /jffs/scripts/dnsmasq.postconf)" -gt 0 ]; then
					sed -i '/exit 0/d' /jffs/scripts/dnsmasq.postconf
				fi
			else
				echo "#!/bin/sh" > /jffs/scripts/dnsmasq.postconf
				echo "" >> /jffs/scripts/dnsmasq.postconf
				echo "cat $DNSCONF >> /etc/dnsmasq.conf # $SCRIPT_NAME" >> /jffs/scripts/dnsmasq.postconf
				chmod 0755 /jffs/scripts/dnsmasq.postconf
			fi
		;;
		delete)
			if [ -f /jffs/scripts/dnsmasq.postconf ]; then
				STARTUPLINECOUNT=$(grep -c "# $SCRIPT_NAME" /jffs/scripts/dnsmasq.postconf)

				if [ "$STARTUPLINECOUNT" -gt 0 ]; then
					sed -i -e '/# '"$SCRIPT_NAME"'/d' /jffs/scripts/dnsmasq.postconf
				fi
			fi
		;;
	esac
}

Auto_ServiceEvent(){
	case $1 in
		create)
			if [ -f /jffs/scripts/service-event ]; then
				STARTUPLINECOUNT=$(grep -c '# '"$SCRIPT_NAME Guest Networks" /jffs/scripts/service-event)
				STARTUPLINECOUNTEX=$(grep -cx 'if echo "$2" | /bin/grep -q "'"$SCRIPT_NAME"'" || { \[ "$1" = "restart" \] && \[ "$2" = "wireless" \]; }; then { /jffs/scripts/'"$SCRIPT_NAME"' service_event "$@" & }; fi # '"$SCRIPT_NAME Guest Networks" /jffs/scripts/service-event)

				if [ "$STARTUPLINECOUNT" -gt 1 ] || { [ "$STARTUPLINECOUNTEX" -eq 0 ] && [ "$STARTUPLINECOUNT" -gt 0 ]; }; then
					sed -i -e '/# '"$SCRIPT_NAME"' Guest Networks/d' /jffs/scripts/service-event
				fi

				if [ "$STARTUPLINECOUNTEX" -eq 0 ]; then
					echo 'if echo "$2" | /bin/grep -q "'"$SCRIPT_NAME"'" || { [ "$1" = "restart" ] && [ "$2" = "wireless" ]; }; then { /jffs/scripts/'"$SCRIPT_NAME"' service_event "$@" & }; fi # '"$SCRIPT_NAME Guest Networks" >> /jffs/scripts/service-event
				fi
			else
				echo "#!/bin/sh" > /jffs/scripts/service-event
				echo "" >> /jffs/scripts/service-event
				echo 'if echo "$2" | /bin/grep -q "'"$SCRIPT_NAME"'" || { [ "$1" = "restart" ] && [ "$2" = "wireless" ]; }; then { /jffs/scripts/'"$SCRIPT_NAME"' service_event "$@" & }; fi # '"$SCRIPT_NAME Guest Networks" >> /jffs/scripts/service-event
				chmod 0755 /jffs/scripts/service-event
			fi
		;;
		delete)
			if [ -f /jffs/scripts/service-event ]; then
				STARTUPLINECOUNT=$(grep -c '# '"$SCRIPT_NAME"' Guest Networks' /jffs/scripts/service-event)

				if [ "$STARTUPLINECOUNT" -gt 0 ]; then
					sed -i -e '/# '"$SCRIPT_NAME"' Guest Networks/d' /jffs/scripts/service-event
				fi
			fi
		;;
	esac
}

Auto_Startup(){
	case $1 in
		create)
			if [ -f /jffs/scripts/firewall-start ]; then
				STARTUPLINECOUNT=$(grep -c '# '"$SCRIPT_NAME"' Guest Networks' /jffs/scripts/firewall-start)
				STARTUPLINECOUNTEX=$(grep -cx "/jffs/scripts/$SCRIPT_NAME runnow & # $SCRIPT_NAME Guest Networks" /jffs/scripts/firewall-start)

				if [ "$STARTUPLINECOUNT" -gt 1 ] || { [ "$STARTUPLINECOUNTEX" -eq 0 ] && [ "$STARTUPLINECOUNT" -gt 0 ]; }; then
					sed -i -e '/# '"$SCRIPT_NAME"' Guest Networks/d' /jffs/scripts/firewall-start
				fi

				if [ "$STARTUPLINECOUNTEX" -eq 0 ]; then
					echo "/jffs/scripts/$SCRIPT_NAME runnow & # $SCRIPT_NAME Guest Networks" >> /jffs/scripts/firewall-start
				fi
			else
				echo "#!/bin/sh" > /jffs/scripts/firewall-start
				echo "" >> /jffs/scripts/firewall-start
				echo "/jffs/scripts/$SCRIPT_NAME runnow & # $SCRIPT_NAME Guest Networks" >> /jffs/scripts/firewall-start
				chmod 0755 /jffs/scripts/firewall-start
			fi
		;;
		delete)
			if [ -f /jffs/scripts/firewall-start ]; then
				STARTUPLINECOUNT=$(grep -c '# '"$SCRIPT_NAME"' Guest Networks' /jffs/scripts/firewall-start)

				if [ "$STARTUPLINECOUNT" -gt 0 ]; then
					sed -i -e '/# '"$SCRIPT_NAME"' Guest Networks/d' /jffs/scripts/firewall-start
				fi
			fi
		;;
	esac
}

Auto_ServiceEventEnd(){
	case $1 in
		create)
			if [ -f /jffs/scripts/firewall-start ]; then
				STARTUPLINECOUNT=$(grep -c '# '"$SCRIPT_NAME"' Guest Networks' /jffs/scripts/firewall-start)

				if [ "$STARTUPLINECOUNT" -gt 0 ]; then
					sed -i -e '/# '"$SCRIPT_NAME"' Guest Networks/d' /jffs/scripts/firewall-start
				fi
			fi
			if [ -f /jffs/scripts/service-event-end ]; then
				STARTUPLINECOUNT=$(grep -c '# '"$SCRIPT_NAME Guest Networks" /jffs/scripts/service-event-end)
				STARTUPLINECOUNTEX=$(grep -cx 'if { \[ "$1" = "start" \] || \[ "$1" = "restart" \]; } && \[ "$2" = "firewall" \]; then { /jffs/scripts/'"$SCRIPT_NAME"' runnow & }; fi # '"$SCRIPT_NAME Guest Networks" /jffs/scripts/service-event-end)

				if [ "$STARTUPLINECOUNT" -gt 1 ] || { [ "$STARTUPLINECOUNTEX" -eq 0 ] && [ "$STARTUPLINECOUNT" -gt 0 ]; }; then
					sed -i -e '/# '"$SCRIPT_NAME"' Guest Networks/d' /jffs/scripts/service-event-end
				fi

				if [ "$STARTUPLINECOUNTEX" -eq 0 ]; then
					echo 'if { [ "$1" = "start" ] || [ "$1" = "restart" ]; } && [ "$2" = "firewall" ]; then { /jffs/scripts/'"$SCRIPT_NAME"' runnow & }; fi # '"$SCRIPT_NAME Guest Networks" >> /jffs/scripts/service-event-end
				fi
			else
				echo "#!/bin/sh" > /jffs/scripts/service-event-end
				echo "" >> /jffs/scripts/service-event-end
				echo 'if { [ "$1" = "start" ] || [ "$1" = "restart" ]; } && [ "$2" = "firewall" ]; then { /jffs/scripts/'"$SCRIPT_NAME"' runnow & }; fi # '"$SCRIPT_NAME Guest Networks" >> /jffs/scripts/service-event-end
				chmod 0755 /jffs/scripts/service-event-end
			fi
		;;
		delete)
			if [ -f /jffs/scripts/firewall-start ]; then
				STARTUPLINECOUNT=$(grep -c '# '"$SCRIPT_NAME"' Guest Networks' /jffs/scripts/firewall-start)

				if [ "$STARTUPLINECOUNT" -gt 0 ]; then
					sed -i -e '/# '"$SCRIPT_NAME"' Guest Networks/d' /jffs/scripts/firewall-start
				fi
			fi
			if [ -f /jffs/scripts/service-event ]; then
				STARTUPLINECOUNT=$(grep -c '# '"$SCRIPT_NAME"' Guest Networks' /jffs/scripts/service-event)

				if [ "$STARTUPLINECOUNT" -gt 0 ]; then
					sed -i -e '/# '"$SCRIPT_NAME"' Guest Networks/d' /jffs/scripts/service-event
				fi
			fi
		;;
	esac
}

Auto_ServiceStart(){
	case $1 in
		create)
			if [ -f /jffs/scripts/services-start ]; then
				STARTUPLINECOUNT=$(grep -c '# '"$SCRIPT_NAME" /jffs/scripts/services-start)
				STARTUPLINECOUNTEX=$(grep -cx "/jffs/scripts/$SCRIPT_NAME startup & # $SCRIPT_NAME" /jffs/scripts/services-start)

				if [ "$STARTUPLINECOUNT" -gt 1 ] || { [ "$STARTUPLINECOUNTEX" -eq 0 ] && [ "$STARTUPLINECOUNT" -gt 0 ]; }; then
					sed -i -e '/# '"$SCRIPT_NAME"'/d' /jffs/scripts/services-start
				fi

				if [ "$STARTUPLINECOUNTEX" -eq 0 ]; then
					echo "/jffs/scripts/$SCRIPT_NAME startup & # $SCRIPT_NAME" >> /jffs/scripts/services-start
				fi
			else
				echo "#!/bin/sh" > /jffs/scripts/services-start
				echo "" >> /jffs/scripts/services-start
				echo "/jffs/scripts/$SCRIPT_NAME startup & # $SCRIPT_NAME" >> /jffs/scripts/services-start
				chmod 0755 /jffs/scripts/services-start
			fi
		;;
		delete)
			if [ -f /jffs/scripts/services-start ]; then
				STARTUPLINECOUNT=$(grep -c '# '"$SCRIPT_NAME" /jffs/scripts/services-start)

				if [ "$STARTUPLINECOUNT" -gt 0 ]; then
					sed -i -e '/# '"$SCRIPT_NAME"'/d' /jffs/scripts/services-start
				fi
			fi
		;;
	esac
}

Auto_OpenVPNEvent(){
	case $1 in
		create)
			if [ -f /jffs/scripts/openvpn-event ]; then
				STARTUPLINECOUNT=$(grep -c '# '"$SCRIPT_NAME" /jffs/scripts/openvpn-event)
				STARTUPLINECOUNTEX=$(grep -cx "/jffs/scripts/$SCRIPT_NAME openvpn "'$1 $script_type & # '"$SCRIPT_NAME" /jffs/scripts/openvpn-event)

				if [ "$STARTUPLINECOUNT" -gt 1 ] || { [ "$STARTUPLINECOUNTEX" -eq 0 ] && [ "$STARTUPLINECOUNT" -gt 0 ]; }; then
					sed -i -e '/# '"$SCRIPT_NAME"'/d' /jffs/scripts/openvpn-event
				fi

				if [ "$STARTUPLINECOUNTEX" -eq 0 ]; then
					sed -i '2 i /jffs/scripts/'"$SCRIPT_NAME"' openvpn $1 $script_type & # '"$SCRIPT_NAME" /jffs/scripts/openvpn-event
				fi
			else
				echo "#!/bin/sh" > /jffs/scripts/openvpn-event
				echo "" >> /jffs/scripts/openvpn-event
				echo "/jffs/scripts/$SCRIPT_NAME openvpn "'$1 $script_type & # '"$SCRIPT_NAME" >> /jffs/scripts/openvpn-event
				chmod 0755 /jffs/scripts/openvpn-event
			fi
		;;
		delete)
			if [ -f /jffs/scripts/openvpn-event ]; then
				STARTUPLINECOUNT=$(grep -c '# '"$SCRIPT_NAME" /jffs/scripts/openvpn-event)

				if [ "$STARTUPLINECOUNT" -gt 0 ]; then
					sed -i -e '/# '"$SCRIPT_NAME"'/d' /jffs/scripts/openvpn-event
				fi
			fi
		;;
	esac
}

Auto_Cron(){
	case $1 in
		create)
			STARTUPLINECOUNT=$(cru l | grep -c "$SCRIPT_NAME")

			if [ "$STARTUPLINECOUNT" -eq 0 ]; then
				cru a "$SCRIPT_NAME" "*/10 * * * * /jffs/scripts/$SCRIPT_NAME check"
			fi
		;;
		delete)
			STARTUPLINECOUNT=$(cru l | grep -c "$SCRIPT_NAME")

			if [ "$STARTUPLINECOUNT" -gt 0 ]; then
				cru d "$SCRIPT_NAME"
			fi
		;;
	esac
}

Avahi_Conf(){
	case $1 in
		create)
			if [ -f /jffs/scripts/avahi-daemon.postconf ]; then
				STARTUPLINECOUNT=$(grep -c "$SCRIPT_NAME" /jffs/scripts/avahi-daemon.postconf)

				if [ "$STARTUPLINECOUNT" -eq 0 ]; then
					{
					echo 'echo "" >> "$1"'
					echo 'echo "[reflector]" >> "$1" # '"$SCRIPT_NAME"
					echo 'echo "enable-reflector=yes" >> "$1" # '"$SCRIPT_NAME"
					echo "sed -i '/^\[Server\]/a cache-entries-max=0' "'"$1" # '"$SCRIPT_NAME"
					} >> /jffs/scripts/avahi-daemon.postconf
					service restart_mdns >/dev/null 2>&1
				fi
			else
				{
				echo '#!/bin/sh'
				echo 'echo "" >> "$1"'
				echo 'echo "[reflector]" >> "$1" # '"$SCRIPT_NAME"
				echo 'echo "enable-reflector=yes" >> "$1" # '"$SCRIPT_NAME"
				echo "sed -i '/^\[Server\]/a cache-entries-max=0' "'"$1" # '"$SCRIPT_NAME"
				} > /jffs/scripts/avahi-daemon.postconf
				chmod 0755 /jffs/scripts/avahi-daemon.postconf
				service restart_mdns >/dev/null 2>&1
			fi
		;;
		delete)
			if [ -f /jffs/scripts/avahi-daemon.postconf ]; then
				STARTUPLINECOUNT=$(grep -c "$SCRIPT_NAME" /jffs/scripts/avahi-daemon.postconf)

				if [ "$STARTUPLINECOUNT" -gt 0 ]; then
					sed -i -e '/# '"$SCRIPT_NAME"'/d' /jffs/scripts/avahi-daemon.postconf
					service restart_mdns >/dev/null 2>&1
				fi
			fi
		;;
	esac
}

### Code for this function courtesy of https://github.com/decoderman- credit to @thelonelycoder ###
Firmware_Version_Check(){
	echo "$1" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'
}
############################################################################

Firmware_Version_WebUI(){
	if nvram get rc_support | grep -qF "am_addons"; then
		return 0
	else
		return 1
	fi
}

### Code for these functions inspired by https://github.com/Adamm00 - credit to @Adamm ###
Check_Lock(){
	if [ -f "/tmp/$SCRIPT_NAME.lock" ]; then
		ageoflock=$(($(date +%s) - $(date +%s -r /tmp/$SCRIPT_NAME.lock)))
		if [ "$ageoflock" -gt 600 ]; then
			Print_Output true "Stale lock file found (>600 seconds old) - purging lock" "$ERR"
			kill "$(sed -n '1p' /tmp/$SCRIPT_NAME.lock)" >/dev/null 2>&1
			Clear_Lock
			echo "$$" > "/tmp/$SCRIPT_NAME.lock"
			return 0
		else
			Print_Output true "Lock file found (age: $ageoflock seconds) - stopping to prevent duplicate runs" "$ERR"
			if [ $# -eq 0 ] || [ -z "$1" ]; then
				exit 1
			else
				return 1
			fi
		fi
	else
		echo "$$" > "/tmp/$SCRIPT_NAME.lock"
		return 0
	fi
}

Clear_Lock(){
	rm -f "/tmp/$SCRIPT_NAME.lock" 2>/dev/null
	return 0
}
############################################################################

Set_Version_Custom_Settings(){
	SETTINGSFILE="/jffs/addons/custom_settings.txt"
	case "$1" in
		local)
			if [ -f "$SETTINGSFILE" ]; then
				if [ "$(grep -c "yazfi_version_local" $SETTINGSFILE)" -gt 0 ]; then
					if [ "$SCRIPT_VERSION" != "$(grep "yazfi_version_local" /jffs/addons/custom_settings.txt | cut -f2 -d' ')" ]; then
						sed -i "s/yazfi_version_local.*/yazfi_version_local $2/" "$SETTINGSFILE"
					fi
				else
					echo "yazfi_version_local $2" >> "$SETTINGSFILE"
				fi
			else
				echo "yazfi_version_local $2" >> "$SETTINGSFILE"
			fi
		;;
		server)
			if [ -f "$SETTINGSFILE" ]; then
				if [ "$(grep -c "yazfi_version_server" $SETTINGSFILE)" -gt 0 ]; then
					if [ "$2" != "$(grep "yazfi_version_server" /jffs/addons/custom_settings.txt | cut -f2 -d' ')" ]; then
						sed -i "s/yazfi_version_server.*/yazfi_version_server $2/" "$SETTINGSFILE"
					fi
				else
					echo "yazfi_version_server $2" >> "$SETTINGSFILE"
				fi
			else
				echo "yazfi_version_server $2" >> "$SETTINGSFILE"
			fi
		;;
	esac
}

Update_Check(){
	echo 'var updatestatus = "InProgress";' > "$SCRIPT_WEB_DIR/detect_update.js"
	doupdate="false"
	localver=$(grep "SCRIPT_VERSION=" "/jffs/scripts/$SCRIPT_NAME" | grep -m1 -oE 'v[0-9]{1,2}([.][0-9]{1,2})([.][0-9]{1,2})')
	/usr/sbin/curl -fsL --retry 3 "$SCRIPT_REPO/404/$SCRIPT_NAME.sh" | grep -qF "jackyaz" || { Print_Output true "404 error detected - stopping update" "$ERR"; return 1; }
	serverver=$(/usr/sbin/curl -fsL --retry 3 "$SCRIPT_REPO/version/$SCRIPT_NAME.sh" | grep "SCRIPT_VERSION=" | grep -m1 -oE 'v[0-9]{1,2}([.][0-9]{1,2})([.][0-9]{1,2})')
	if [ "$localver" != "$serverver" ]; then
		doupdate="version"
		Set_Version_Custom_Settings server "$serverver"
		echo 'var updatestatus = "'"$serverver"'";'  > "$SCRIPT_WEB_DIR/detect_update.js"
	else
		localmd5="$(md5sum "/jffs/scripts/$SCRIPT_NAME" | awk '{print $1}')"
		remotemd5="$(curl -fsL --retry 3 "$SCRIPT_REPO/md5/$SCRIPT_NAME.sh" | md5sum | awk '{print $1}')"
		if [ "$localmd5" != "$remotemd5" ]; then
			doupdate="md5"
			Set_Version_Custom_Settings server "$serverver-hotfix"
			echo 'var updatestatus = "'"$serverver-hotfix"'";'  > "$SCRIPT_WEB_DIR/detect_update.js"
		fi
	fi
	if [ "$doupdate" = "false" ]; then
		echo 'var updatestatus = "None";'  > "$SCRIPT_WEB_DIR/detect_update.js"
	fi
	echo "$doupdate,$localver,$serverver"
}

##----------------------------------------##
## Modified by Martinski W. [2022-Dec-26] ##
##----------------------------------------##
Update_Version(){
	if [ $# -eq 0 ] || [ -z "$1" ]; then
		updatecheckresult="$(Update_Check)"
		isupdate="$(echo "$updatecheckresult" | cut -f1 -d',')"
		localver="$(echo "$updatecheckresult" | cut -f2 -d',')"
		serverver="$(echo "$updatecheckresult" | cut -f3 -d',')"

		if [ "$isupdate" = "version" ]; then
			Print_Output true "New version of $SCRIPT_NAME available - $serverver" "$PASS"
		elif [ "$isupdate" = "md5" ]; then
			Print_Output true "MD5 hash of $SCRIPT_NAME does not match - hotfix available - $serverver" "$PASS"
		fi

		if [ "$isupdate" != "false" ]; then
			printf "\\n${BOLD}Do you want to continue with the update? (y/n)${CLEARFORMAT}  "
			read -r confirm
			case "$confirm" in
				y|Y)
					if Firmware_Version_WebUI ; then
						Update_File shared-jy.tar.gz
						Update_File YazFi_www.asp
					else
						Print_Output true "WebUI is only supported on firmware versions with addon support" "$WARN"
					fi

					Update_File README.md
					Update_File LICENSE

					##-------------------------------------##
					## Added by Martinski W. [2022-Dec-26] ##
					##-------------------------------------##
					Update_File "$SCRIPT_CONF"

					Download_File "$SCRIPT_REPO/update/$SCRIPT_NAME.sh" "/jffs/scripts/$SCRIPT_NAME" && Print_Output true "$SCRIPT_NAME successfully updated - restarting firewall to apply update"
					chmod 0755 "/jffs/scripts/$SCRIPT_NAME"
					Set_Version_Custom_Settings local "$serverver"
					Set_Version_Custom_Settings server "$serverver"
					Clear_Lock
					service restart_firewall >/dev/null 2>&1
					PressEnter
					exec "$0"
					exit 0
				;;
				*)
					printf "\\n"
					Clear_Lock
					return 1
				;;
			esac
		else
			Print_Output true "No updates available - latest is $localver" "$WARN"
			Clear_Lock
		fi
	fi

	if [ "$1" = "force" ]; then
		serverver=$(/usr/sbin/curl -fsL --retry 3 "$SCRIPT_REPO/version/$SCRIPT_NAME.sh" | grep "SCRIPT_VERSION=" | grep -m1 -oE 'v[0-9]{1,2}([.][0-9]{1,2})([.][0-9]{1,2})')
		Print_Output true "Downloading latest version ($serverver) of $SCRIPT_NAME" "$PASS"
		if Firmware_Version_WebUI ; then
			Update_File shared-jy.tar.gz
			Update_File YazFi_www.asp
		else
			Print_Output true "WebUI is only supported on firmware versions with addon support" "$WARN"
		fi
		Update_File README.md
		Update_File LICENSE

		##-------------------------------------##
		## Added by Martinski W. [2022-Dec-26] ##
		##-------------------------------------##
		Update_File "$SCRIPT_CONF"

		Download_File "$SCRIPT_REPO/update/$SCRIPT_NAME.sh" "/jffs/scripts/$SCRIPT_NAME" && Print_Output true "$SCRIPT_NAME successfully updated - restarting firewall to apply update"
		chmod 0755 "/jffs/scripts/$SCRIPT_NAME"
		Set_Version_Custom_Settings local "$serverver"
		Set_Version_Custom_Settings server "$serverver"
		Clear_Lock
		service restart_firewall >/dev/null 2>&1
		if [ -z "$2" ]; then
			PressEnter
			exec "$0"
		elif [ "$2" = "unattended" ]; then
			exec "$0" postupdate
		fi
		exit 0
	fi
}

##----------------------------------------##
## Modified by Martinski W. [2022-Dec-26] ##
##----------------------------------------##
Update_File(){
	if [ "$1" = "YazFi_www.asp" ]; then
		tmpfile="/tmp/$1"
		Download_File "$SCRIPT_REPO/files/$1" "$tmpfile"
		if ! diff -q "$tmpfile" "$SCRIPT_DIR/$1" >/dev/null 2>&1; then
			if [ -f "$SCRIPT_DIR/$1" ]; then
				Get_WebUI_Page "$SCRIPT_DIR/$1"
				sed -i "\\~$MyPage~d" /tmp/menuTree.js
				rm -f "$SCRIPT_WEBPAGE_DIR/$MyPage" 2>/dev/null
			fi
			Download_File "$SCRIPT_REPO/files/$1" "$SCRIPT_DIR/$1"
			Print_Output true "New version of $1 downloaded" "$PASS"
			Mount_WebUI
		fi
		rm -f "$tmpfile"
	elif [ "$1" = "shared-jy.tar.gz" ]; then
		if [ ! -f "$SHARED_DIR/$1.md5" ]; then
			Download_File "$SHARED_REPO/$1" "$SHARED_DIR/$1"
			Download_File "$SHARED_REPO/$1.md5" "$SHARED_DIR/$1.md5"
			tar -xzf "$SHARED_DIR/$1" -C "$SHARED_DIR"
			rm -f "$SHARED_DIR/$1"
			Print_Output true "New version of $1 downloaded" "$PASS"
		else
			localmd5="$(cat "$SHARED_DIR/$1.md5")"
			remotemd5="$(curl -fsL --retry 3 "$SHARED_REPO/$1.md5")"
			if [ "$localmd5" != "$remotemd5" ]; then
				Download_File "$SHARED_REPO/$1" "$SHARED_DIR/$1"
				Download_File "$SHARED_REPO/$1.md5" "$SHARED_DIR/$1.md5"
				tar -xzf "$SHARED_DIR/$1" -C "$SHARED_DIR"
				rm -f "$SHARED_DIR/$1"
				Print_Output true "New version of $1 downloaded" "$PASS"
			fi
		fi
	elif [ "$1" = "README.md" ] || [ "$1" = "LICENSE" ]; then
		tmpfile="/tmp/$1"
		Download_File "$SCRIPT_REPO/files/$1" "$tmpfile"
		if ! diff -q "$tmpfile" "$SCRIPT_DIR/$1" >/dev/null 2>&1; then
			Download_File "$SCRIPT_REPO/files/$1" "$SCRIPT_DIR/$1"
		fi
		rm -f "$tmpfile"
	elif [ "$1" = "$SCRIPT_CONF" ]
	then
		if ! grep -q -E "^wl31_|^wl32_|^wl33_" "$SCRIPT_CONF"
		then
			if Conf_ADD_Download "$SCRIPT_CONF"
			then
				cat "${SCRIPT_CONF}.ADD.txt" >> "$SCRIPT_CONF"
				cp -fp "$SCRIPT_CONF" "${SCRIPT_CONF}.bak"
				rm -f "${SCRIPT_CONF}.ADD.txt"
			else
				Print_Output true "Could not update configuration file: $SCRIPT_CONF" "$ERR"
			fi
		fi
	else
		return 1
	fi
}

IP_Local(){
	if echo "$1" | grep -qE '(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)'; then
		return 0
	elif [ "$1" = "127.0.0.1" ]; then
		return 0
	else
		return 1
	fi
}

IP_Router(){
	if [ "$1" = "$(nvram get lan_ipaddr)" ] || [ "$1" = "127.0.0.1" ]; then
		return 0
	elif [ "$1" = "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").$(nvram get lan_ipaddr | cut -f4 -d".")" ]; then
		return 0
	else
		return 1
	fi
}

Validate_Enabled_IFACE(){
	IFACE_TEST="$(nvram get "${1}_bss_enabled")"
	if ! Validate_Number "" "$IFACE_TEST" silent; then IFACE_TEST=0; fi
	if [ "$IFACE_TEST" -eq 0 ]; then
		if [ -z "$2" ]; then
			Print_Output false "$1 - Interface not enabled/configured in Web GUI (Guest Network menu)" "$ERR"
		fi
		return 1
	else
		return 0
	fi
}

Validate_Exists_IFACE(){
	validiface=""
	for IFACE_EXIST in $IFACELIST; do
		if [ "$1" = "$IFACE_EXIST" ]; then
			validiface="true"
		fi
	done

	if [ "$validiface" = "true" ]; then
		return 0
	else
		if [ -z "$2" ]; then
			Print_Output false "$1 - Interface not supported on this router" "$ERR"
		fi
		return 1
	fi
}

Validate_IP(){
	if expr "$2" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; then
		for i in 1 2 3 4; do
			if [ "$(echo "$2" | cut -d. -f$i)" -gt 255 ]; then
				Print_Output false "$1 - Octet $i ($(echo "$2" | cut -d. -f$i)) - is invalid, must be less than 255" "$ERR"
				return 1
			fi
		done

		if [ "$3" != "DNS" ]; then
			if IP_Local "$2"; then
				return 0
			else
				Print_Output false "$1 - $2 - Non-local IP address block used" "$ERR"
				return 1
			fi
		else
			return 0
		fi
	else
		Print_Output false "$1 - $2 - is not a valid IPv4 address, valid format is 1.2.3.4" "$ERR"
		return 1
	fi
}

Validate_Number(){
	if [ "$2" -eq "$2" ] 2>/dev/null; then
		return 0
	else
		formatted="$(echo "$1" | sed -e 's/|/ /g')"
		if [ -z "$3" ]; then
			Print_Output false "$formatted - $2 is not a number" "$ERR"
		fi
		return 1
	fi
}

##----------------------------------------##
## Modified by Martinski W. [2022-Dec-05] ##
##----------------------------------------##
Validate_DHCP(){
	if ! Validate_Number "$1" "$2"; then
		return 1
	elif ! Validate_Number "$1" "$3"; then
		return 1
	fi

	if [ "$2" -gt "$3" ] || { [ "$2" -lt 2 ] || [ "$2" -gt 253 ]; } || { [ "$3" -lt 3 ] || [ "$3" -gt 254 ]; }; then
		Print_Output false "$1 - $2 to $3 - both numbers must be between 2 and 254, $2 must be less than $3" "$ERR"
		return 1
	else
		return 0
	fi
}

##----------------------------------------------##
## Added/modified by Martinski W. [2023-Jan-30] ##
##----------------------------------------------##
# The DHCP Lease Time values can be given in:
# seconds, minutes, hours, days, or weeks.
# Single '0' or 'I' indicates "infinite" value.
#------------------------------------------------#
Validate_DHCP_LeaseTime()
{
	timeUnits="X"  timeFactor=1  timeNumber="$2"

	if [ "$2" = "0" ] || [ "$2" = "$InfiniteLeaseTimeTag" ]
	then return 0 ; fi

	if echo "$2" | grep -q "^0.*"
	then
		Print_Output false "$1 - $2 is not a valid setting." "$ERR"
		return 1
	fi

	if echo "$2" | grep -q "^[0-9]\{1,7\}$"
	then
		timeUnits="s"
		timeNumber="$2"
	elif echo "$2" | grep -q "^[0-9]\{1,6\}[smhdw]\{1\}$"
	then
		timeUnits="$(echo "$2" | awk '{print substr($0,length($0),1)}')"
		timeNumber="$(echo "$2" | awk '{print substr($0,0,length($0)-1)}')"
	fi

	case "$timeUnits" in
		s) timeFactor=1 ;;
		m) timeFactor=60 ;;
		h) timeFactor=3600 ;;
		d) timeFactor=86400 ;;
		w) timeFactor=604800 ;;
	esac

	if ! Validate_Number "$1" "$timeNumber"
	then return 1 ; fi

	timeValue="$((timeNumber * timeFactor))"

	if [ "$timeValue" -lt "$MinDHCPLeaseTime" ] || [ "$timeValue" -gt "$MaxDHCPLeaseTime" ]; then
		Print_Output false "$1 - $timeValue - must be between $MinDHCPLeaseTime and $MaxDHCPLeaseTime in seconds." "$ERR"
		return 1
	else
		return 0
	fi
}

Validate_VPNClientNo(){
	if ! Validate_Number "$1" "$2"; then
		return 1
	fi

	if [ "$2" -lt 1 ] || [ "$2" -gt 5 ]; then
		Print_Output false "$1 - $2 - must be between 1 and 5" "$ERR"
		return 1
	else
		return 0
	fi
}

Validate_TrueFalse(){
	case "$2" in
		true|TRUE|false|FALSE)
			return 0
		;;
		*)
			Print_Output false "$1 - $2 - must be either true or false" "$ERR"
			return 1
		;;
	esac
}

Validate_String(){
		if expr "$1" : '[a-zA-Z0-9][a-zA-Z0-9]*$' >/dev/null; then
			return 0
		else
			Print_Output false "String contains non-alphanumeric characters, these will be removed" "$ERR"
			return 1
		fi
}

Conf_FromSettings(){
	SETTINGSFILE="/jffs/addons/custom_settings.txt"
	TMPFILE="/tmp/yazfi_settings.txt"
	if [ -f "$SETTINGSFILE" ]; then
		if [ "$(grep "yazfi_" $SETTINGSFILE | grep -v "version" -c)" -gt 0 ]; then
			Print_Output true "Updated settings from WebUI found, merging into $SCRIPT_CONF" "$PASS"
			cp -a "$SCRIPT_CONF" "$SCRIPT_CONF.bak"
			grep "yazfi_" "$SETTINGSFILE" | grep -v "version" > "$TMPFILE"
			sed -i "s/yazfi_//g;s/ /=/g" "$TMPFILE"
			while IFS='' read -r line || [ -n "$line" ]; do
				SETTINGNAME="$(echo "$line" | cut -f1 -d'=' | awk 'BEGIN{FS="_"}{ print $1 "_" toupper($2) }')"
				SETTINGVALUE="$(echo "$line" | cut -f2 -d'=')"
				sed -i "s/$SETTINGNAME=.*/$SETTINGNAME=$SETTINGVALUE/" "$SCRIPT_CONF"
			done < "$TMPFILE"
			grep 'yazfi_version' "$SETTINGSFILE" > "$TMPFILE"
			sed -i "\\~yazfi_~d" "$SETTINGSFILE"
			mv "$SETTINGSFILE" "$SETTINGSFILE.bak"
			cat "$SETTINGSFILE.bak" "$TMPFILE" > "$SETTINGSFILE"
			rm -f "$TMPFILE"
			rm -f "$SETTINGSFILE.bak"
			Print_Output true "Merge of updated settings from WebUI completed successfully" "$PASS"
		else
			Print_Output false "No updated settings from WebUI found, no merge into $SCRIPT_CONF necessary" "$PASS"
		fi
	fi
}

Conf_FixBlanks(){
	if ! Conf_Exists; then
		Conf_Download "$SCRIPT_CONF"
		Clear_Lock
		return 1
	fi

	##-------------------------------------##
	## Added by Martinski W. [2022-Dec-07] ##
	##-------------------------------------##
	. "$SCRIPT_CONF"

	cp -a "$SCRIPT_CONF" "$SCRIPT_CONF.bak"

	for IFACEBLANK in $IFACELIST_FULL; do
		IFACETMPBLANK="$(Get_Iface_Var "$IFACEBLANK")"
		IPADDRTMPBLANK=""

		if [ -z "$(eval echo '$'"${IFACETMPBLANK}_IPADDR")" ]; then
			IPADDRTMPBLANK="192.168.0"

			COUNTER=0
			until [ "$(grep -o "$IPADDRTMPBLANK" $SCRIPT_CONF | wc -l)" -eq 0 ] && [ "$(ifconfig -a | grep -o "$IPADDRTMPBLANK" | wc -l )" -eq 0 ]; do
				IPADDRTMPBLANK="192.168.$COUNTER"
				COUNTER=$((COUNTER + 1))
			done

			sed -i -e "s/${IFACETMPBLANK}_IPADDR=/${IFACETMPBLANK}_IPADDR=${IPADDRTMPBLANK}.0/" "$SCRIPT_CONF"
			Print_Output false "${IFACETMPBLANK}_IPADDR is blank, setting to next available subnet" "$WARN"
		fi

		if [ -z "$(eval echo '$'"${IFACETMPBLANK}_DHCPSTART")" ]; then
			sed -i -e "s/${IFACETMPBLANK}_DHCPSTART=/${IFACETMPBLANK}_DHCPSTART=2/" "$SCRIPT_CONF"
			Print_Output false "${IFACETMPBLANK}_DHCPSTART is blank, setting to 2" "$WARN"
		fi

		if [ -z "$(eval echo '$'"${IFACETMPBLANK}_DHCPEND")" ]; then
			sed -i -e "s/${IFACETMPBLANK}_DHCPEND=/${IFACETMPBLANK}_DHCPEND=254/" "$SCRIPT_CONF"
			Print_Output false "${IFACETMPBLANK}_DHCPEND is blank, setting to 254" "$WARN"
		fi

		##----------------------------------------##
		## Modified by Martinski W. [2022-Dec-23] ##
		##----------------------------------------##
		if [ -z "$(eval echo '$'"${IFACETMPBLANK}_DHCPLEASE")" ] || \
		   ! grep -q "${IFACETMPBLANK}_DHCPLEASE=" "$SCRIPT_CONF"
		then
			DHCP_LEASE_VAL="$(nvram get dhcp_lease)"
			if grep -q "${IFACETMPBLANK}_DHCPLEASE=" "$SCRIPT_CONF"
			then
				OUTmsg="is blank, setting to $DHCP_LEASE_VAL seconds"
				sed -i -e "s/${IFACETMPBLANK}_DHCPLEASE=/${IFACETMPBLANK}_DHCPLEASE=${DHCP_LEASE_VAL}/" "$SCRIPT_CONF"
			else
				OUTmsg="is not found."
				NUMLINE="$(grep -n "${IFACETMPBLANK}_DHCPEND=" "$SCRIPT_CONF" | awk -F ':' '{print $1}')"
				if [ -n "$NUMLINE" ]
				then
					OUTmsg="is not found, setting to $DHCP_LEASE_VAL seconds"
					sed -i "$((NUMLINE + 1)) i ${IFACETMPBLANK}_DHCPLEASE=${DHCP_LEASE_VAL}" "$SCRIPT_CONF"
				fi
			fi
			Print_Output false "${IFACETMPBLANK}_DHCPLEASE ${OUTmsg}" "$WARN"
		fi

		if [ -z "$(eval echo '$'"${IFACETMPBLANK}_DNS1")" ]; then
			if [ -n "$(eval echo '$'"${IFACETMPBLANK}_IPADDR")" ]; then
				sed -i -e "s/${IFACETMPBLANK}_DNS1=/${IFACETMPBLANK}_DNS1=$(eval echo '$'"${IFACETMPBLANK}_IPADDR" | cut -f1-3 -d".").$(nvram get lan_ipaddr | cut -f4 -d".")/" "$SCRIPT_CONF"
				Print_Output false "${IFACETMPBLANK}_DNS1 is blank, setting to $(eval echo '$'"${IFACETMPBLANK}_IPADDR" | cut -f1-3 -d".").$(nvram get lan_ipaddr | cut -f4 -d".")" "$WARN"
			else
				sed -i -e "s/${IFACETMPBLANK}_DNS1=/${IFACETMPBLANK}_DNS1=$IPADDRTMPBLANK.$(nvram get lan_ipaddr | cut -f4 -d".")/" "$SCRIPT_CONF"
				Print_Output false "${IFACETMPBLANK}_DNS1 is blank, setting to $IPADDRTMPBLANK.$(nvram get lan_ipaddr | cut -f4 -d".")" "$WARN"
			fi
		fi

		if [ -z "$(eval echo '$'"${IFACETMPBLANK}_DNS2")" ]; then
			if [ -n "$(eval echo '$'"${IFACETMPBLANK}_IPADDR")" ]; then
				sed -i -e "s/${IFACETMPBLANK}_DNS2=/${IFACETMPBLANK}_DNS2=$(eval echo '$'"${IFACETMPBLANK}_IPADDR" | cut -f1-3 -d".").$(nvram get lan_ipaddr | cut -f4 -d".")/" "$SCRIPT_CONF"
				Print_Output false "${IFACETMPBLANK}_DNS2 is blank, setting to $(eval echo '$'"${IFACETMPBLANK}_IPADDR" | cut -f1-3 -d".").$(nvram get lan_ipaddr | cut -f4 -d".")" "$WARN"
			else
				sed -i -e "s/${IFACETMPBLANK}_DNS2=/${IFACETMPBLANK}_DNS2=$IPADDRTMPBLANK.$(nvram get lan_ipaddr | cut -f4 -d".")/" "$SCRIPT_CONF"
				Print_Output false "${IFACETMPBLANK}_DNS2 is blank, setting to $IPADDRTMPBLANK.$(nvram get lan_ipaddr | cut -f4 -d".")" "$WARN"
			fi
		fi

		if [ -z "$(eval echo '$'"${IFACETMPBLANK}_FORCEDNS")" ]; then
			sed -i -e "s/${IFACETMPBLANK}_FORCEDNS=/${IFACETMPBLANK}_FORCEDNS=false/" "$SCRIPT_CONF"
			Print_Output false "${IFACETMPBLANK}_FORCEDNS is blank, setting to false" "$WARN"
		fi

		if [ -z "$(eval echo '$'"${IFACETMPBLANK}_ALLOWINTERNET")" ]; then
			ALLOWINTERNETTMP="true"
			sed -i -e "s/${IFACETMPBLANK}_ALLOWINTERNET=/${IFACETMPBLANK}_ALLOWINTERNET=true/" "$SCRIPT_CONF"
			Print_Output false "${IFACETMPBLANK}_ALLOWINTERNET is blank, setting to true" "$WARN"
		fi

		if [ -z "$(eval echo '$'"${IFACETMPBLANK}_REDIRECTALLTOVPN")" ]; then
			REDIRECTTMP="false"
			sed -i -e "s/${IFACETMPBLANK}_REDIRECTALLTOVPN=/${IFACETMPBLANK}_REDIRECTALLTOVPN=false/" "$SCRIPT_CONF"
			Print_Output false "${IFACETMPBLANK}_REDIRECTALLTOVPN is blank, setting to false" "$WARN"
		fi

		if [ -z "$(eval echo '$'"${IFACETMPBLANK}_VPNCLIENTNUMBER")" ]; then
			sed -i -e "s/${IFACETMPBLANK}_VPNCLIENTNUMBER=/${IFACETMPBLANK}_VPNCLIENTNUMBER=1/" "$SCRIPT_CONF"
			Print_Output false "${IFACETMPBLANK}_VPNCLIENTNUMBER is blank, setting to 1" "$WARN"
		fi

		if [ -z "$(eval echo '$'"${IFACETMPBLANK}_TWOWAYTOGUEST")" ]; then
			sed -i -e "s/${IFACETMPBLANK}_TWOWAYTOGUEST=/${IFACETMPBLANK}_TWOWAYTOGUEST=false/" "$SCRIPT_CONF"
			Print_Output false "${IFACETMPBLANK}_TWOWAYTOGUEST is blank, setting to false" "$WARN"
		fi

		if [ -z "$(eval echo '$'"${IFACETMPBLANK}_ONEWAYTOGUEST")" ]; then
			sed -i -e "s/${IFACETMPBLANK}_ONEWAYTOGUEST=/${IFACETMPBLANK}_ONEWAYTOGUEST=false/" "$SCRIPT_CONF"
			Print_Output false "${IFACETMPBLANK}_ONEWAYTOGUEST is blank, setting to false" "$WARN"
		fi

		if [ -z "$(eval echo '$'"${IFACETMPBLANK}_CLIENTISOLATION")" ]; then
			sed -i -e "s/${IFACETMPBLANK}_CLIENTISOLATION=/${IFACETMPBLANK}_CLIENTISOLATION=false/" "$SCRIPT_CONF"
			Print_Output false "${IFACETMPBLANK}_CLIENTISOLATION is blank, setting to false" "$WARN"
		fi
	done

	##-------------------------------------##
	## Added by Martinski W. [2022-Dec-07] ##
	##-------------------------------------##
	if ! diff -q "$SCRIPT_CONF" "$SCRIPT_CONF.bak" >/dev/null 2>&1
	then . "$SCRIPT_CONF" ; fi
}

Conf_Validate(){
	CONF_VALIDATED="true"
	NETWORKS_ENABLED="false"

	Conf_FixBlanks

	for IFACE in $IFACELIST_FULL; do
		IFACETMP="$(Get_Iface_Var "$IFACE")"
		IPADDRTMP=""
		ENABLEDTMP=""
		REDIRECTTMP=""
		IFACE_PASS="true"

		if [ -z "$(eval echo '$'"${IFACETMP}_ENABLED")" ]; then
			ENABLEDTMP="false"
			sed -i -e "s/${IFACETMP}_ENABLED=/${IFACETMP}_ENABLED=false/" "$SCRIPT_CONF"
			Print_Output false "${IFACETMP}_ENABLED is blank, setting to false" "$WARN"
		elif ! Validate_TrueFalse "${IFACETMP}_ENABLED" "$(eval echo '$'"${IFACETMP}_ENABLED")"; then
			ENABLEDTMP="false"
			IFACE_PASS="false"
		else
			ENABLEDTMP="$(eval echo '$'"${IFACETMP}_ENABLED")"
		fi

		if ! Validate_Exists_IFACE "$IFACE" silent && [ "$ENABLEDTMP" = "true" ]; then
			IFACE_PASS="false"
			Print_Output false "$IFACE - Interface not supported on this router" "$ERR"
		else
			if [ "$ENABLEDTMP" = "true" ]; then
				NETWORKS_ENABLED="true"

				if ! Validate_Enabled_IFACE "$IFACE"; then
					IFACE_PASS="false"
				fi

				if [ "$(eval echo '$'"${IFACETMP}_ENABLED")" = "true" ]; then
					if ! Validate_IP "${IFACETMP}_IPADDR" "$(eval echo '$'"${IFACETMP}_IPADDR")"; then
						IFACE_PASS="false"
					else
						IPADDRTMP="$(eval echo '$'"${IFACETMP}_IPADDR" | cut -f1-3 -d".")"

						if [ "$(eval echo '$'"${IFACETMP}_IPADDR" | cut -f4 -d".")" -ne 0 ]; then
							sed -i -e "s/${IFACETMP}_IPADDR=$(eval echo '$'"${IFACETMP}_IPADDR")/${IFACETMP}_IPADDR=$IPADDRTMP.0/" "$SCRIPT_CONF"
							Print_Output false "${IFACETMP}_IPADDR setting last octet to 0" "$WARN"
						fi

						if [ "$(ifconfig -a | grep -o "inet addr:$IPADDRTMP.$(nvram get lan_ipaddr | cut -f4 -d'.')"  | sed 's/inet addr://' | wc -l )" -gt 1 ]; then
							Print_Output false "${IFACETMP}_IPADDR ($(eval echo '$'"${IFACETMP}_IPADDR")) has been used for another interface already" "$ERR"
							IFACE_PASS="false"
						fi
					fi

					if [ -n "$(eval echo '$'"${IFACETMP}_DHCPSTART")" ] && [ -n "$(eval echo '$'"${IFACETMP}_DHCPEND")" ]; then
						if ! Validate_DHCP "${IFACETMP}_DHCPSTART|and|${IFACETMP}_DHCPEND" "$(eval echo '$'"${IFACETMP}_DHCPSTART")" "$(eval echo '$'"${IFACETMP}_DHCPEND")"; then
						IFACE_PASS="false"
						fi
					fi

					##-------------------------------------##
					## Added by Martinski W. [2022-Dec-01] ##
					##-------------------------------------##
					if [ -n "$(eval echo '$'"${IFACETMP}_DHCPLEASE")" ]
					then
						if ! Validate_DHCP_LeaseTime "${IFACETMP}_DHCPLEASE" "$(eval echo '$'"${IFACETMP}_DHCPLEASE")"; then
							IFACE_PASS="false"
						fi
					fi

					if ! Validate_TrueFalse "${IFACETMP}_FORCEDNS" "$(eval echo '$'"${IFACETMP}_FORCEDNS")"; then
						IFACE_PASS="false"
					else
						if [ "$(eval echo '$'"${IFACETMP}_FORCEDNS")" = "true" ]; then
							Print_Output false "$IFACE has FORCEDNS enabled, setting DNS2 to match DNS1..." "$WARN"
							sed -i -e "s/${IFACETMP}_DNS2=.*/${IFACETMP}_DNS2=$(eval echo '$'"${IFACETMP}_DNS1")/" "$SCRIPT_CONF"
						fi
					fi

					if ! Validate_IP "${IFACETMP}_DNS1" "$(eval echo '$'"${IFACETMP}_DNS1")" "DNS"; then
						IFACE_PASS="false"
					fi

					if ! Validate_IP "${IFACETMP}_DNS2" "$(eval echo '$'"${IFACETMP}_DNS2")" "DNS"; then
						IFACE_PASS="false"
					fi

					if ! Validate_TrueFalse "${IFACETMP}_ALLOWINTERNET" "$(eval echo '$'"${IFACETMP}_ALLOWINTERNET")"; then
						ALLOWINTERNETTMP="true"
						IFACE_PASS="false"
					else
						ALLOWINTERNETTMP="$(eval echo '$'"${IFACETMP}_ALLOWINTERNET")"
					fi

					if [ "$ALLOWINTERNETTMP" = "false" ] && ! IP_Local "$(eval echo '$'"${IFACETMP}_DNS1")"; then
						Print_Output false "$IFACE has internet access disabled and a non-local IP has been set for DNS1" "$ERR"
						IFACE_PASS="false"
					fi

					if [ "$ALLOWINTERNETTMP" = "false" ] && ! IP_Local "$(eval echo '$'"${IFACETMP}_DNS2")"; then
						Print_Output false "$IFACE has internet access disabled and a non-local IP has been set for DNS2" "$ERR"
						IFACE_PASS="false"
					fi

					if ! Validate_TrueFalse "${IFACETMP}_REDIRECTALLTOVPN" "$(eval echo '$'"${IFACETMP}_REDIRECTALLTOVPN")"; then
						REDIRECTTMP="false"
						IFACE_PASS="false"
					else
						REDIRECTTMP="$(eval echo '$'"${IFACETMP}_REDIRECTALLTOVPN")"
					fi

					if [ "$REDIRECTTMP" = "true" ] && [ "$ALLOWINTERNETTMP" = "true" ]; then
						if ! Validate_VPNClientNo "${IFACETMP}_VPNCLIENTNUMBER" "$(eval echo '$'"${IFACETMP}_VPNCLIENTNUMBER")"; then
							IFACE_PASS="false"
						else
							if [ "$(nvram get vpn_client"$(eval echo '$'"${IFACETMP}_VPNCLIENTNUMBER")"_rgw)" -ne 2 ]; then
								Print_Output false "VPN Client $(eval echo '$'"${IFACETMP}_VPNCLIENTNUMBER") is not configured for Policy Routing, enabling it..." "$WARN"
								nvram set vpn_client"$(eval echo '$'"${IFACETMP}_VPNCLIENTNUMBER")"_rgw=2
								nvram commit
							fi
						fi
					fi

					if ! Validate_TrueFalse "${IFACETMP}_TWOWAYTOGUEST" "$(eval echo '$'"${IFACETMP}_TWOWAYTOGUEST")"; then
						IFACE_PASS="false"
					fi

					if ! Validate_TrueFalse "${IFACETMP}_ONEWAYTOGUEST" "$(eval echo '$'"${IFACETMP}_ONEWAYTOGUEST")"; then
						IFACE_PASS="false"
					fi

					if [ "$(eval echo '$'"${IFACETMP}_ONEWAYTOGUEST")" = "true" ] && [ "$(eval echo '$'"${IFACETMP}_TWOWAYTOGUEST")" = "true" ]; then
						Print_Output false "$(eval echo '$'"${IFACETMP}_ONEWAYTOGUEST") & $(eval echo '$'"${IFACETMP}_TWOWAYTOGUEST") cannot both be true" "$ERR"
						IFACE_PASS="false"
					fi

					if ! Validate_TrueFalse "${IFACETMP}_CLIENTISOLATION" "$(eval echo '$'"${IFACETMP}_CLIENTISOLATION")"; then
						IFACE_PASS="false"
					fi

					if [ "$(Firmware_Version_Check "$(nvram get buildno)")" -lt "$(Firmware_Version_Check 386.1)" ]; then
						if [ "$ROUTER_MODEL" = "RT-AX88U" ] || [ "$ROUTER_MODEL" = "RT-AX3000" ]; then
							sed -i -e "s/${IFACETMP}_CLIENTISOLATION=true/${IFACETMP}_CLIENTISOLATION=false/" "$SCRIPT_CONF"
						fi
					fi

					if [ "$IFACE_PASS" = "true" ]; then
						Print_Output false "$IFACE passed validation" "$PASS"
					fi
				fi
			fi
		fi

		if [ "$IFACE_PASS" = "false" ]; then
			IFACELIST="$(echo "$IFACELIST" | sed 's/'"$IFACE"'//;s/  / /')"
			Print_Output false "$IFACE failed validation, removing from list" "$CRIT"
		fi
	done

	if [ "$NETWORKS_ENABLED" = "false" ]; then
		Print_Output true "No $SCRIPT_NAME guests are enabled in the configuration file!" "$CRIT"
	fi

	return 0
}

Create_Dirs(){
	if [ ! -d "$SCRIPT_DIR" ]; then
		mkdir -p "$SCRIPT_DIR"
	fi

	if [ ! -d "$USER_SCRIPT_DIR" ]; then
		mkdir -p "$USER_SCRIPT_DIR"
	fi

	if [ ! -d "$SCRIPT_WEBPAGE_DIR" ]; then
		mkdir -p "$SCRIPT_WEBPAGE_DIR"
	fi

	if [ ! -d "$SCRIPT_WEB_DIR" ]; then
		mkdir -p "$SCRIPT_WEB_DIR"
	fi

	if [ ! -d "$SHARED_DIR" ]; then
		mkdir -p "$SHARED_DIR"
	fi
}

Create_Symlinks(){
	rm -f "$SCRIPT_WEB_DIR/"* 2>/dev/null

	ln -s "$SCRIPT_DIR/config"  "$SCRIPT_WEB_DIR/config.htm" 2>/dev/null
	ln -s "$SCRIPT_DIR/.connectedclients" "$SCRIPT_WEB_DIR/connectedclients.htm" 2>/dev/null

	if [ ! -d "$SHARED_WEB_DIR" ]; then
		ln -s "$SHARED_DIR" "$SHARED_WEB_DIR" 2>/dev/null
	fi
}

Download_File(){
	/usr/sbin/curl -fsL --retry 3 "$1" -o "$2"
}

### function based on @dave14305's FlexQoS webconfigpage function ###
Get_WebUI_URL(){
	urlpage=""
	urlproto=""
	urldomain=""
	urlport=""

	urlpage="$(sed -nE "/$SCRIPT_NAME/ s/.*url\: \"(user[0-9]+\.asp)\".*/\1/p" /tmp/menuTree.js)"
	if [ "$(nvram get http_enable)" -eq 1 ]; then
		urlproto="https"
	else
		urlproto="http"
	fi
	if [ -n "$(nvram get lan_domain)" ]; then
		urldomain="$(nvram get lan_hostname).$(nvram get lan_domain)"
	else
		urldomain="$(nvram get lan_ipaddr)"
	fi
	if [ "$(nvram get ${urlproto}_lanport)" -eq 80 ] || [ "$(nvram get ${urlproto}_lanport)" -eq 443 ]; then
		urlport=""
	else
		urlport=":$(nvram get ${urlproto}_lanport)"
	fi

	if echo "$urlpage" | grep -qE "user[0-9]+\.asp"; then
		echo "${urlproto}://${urldomain}${urlport}/${urlpage}" | tr "A-Z" "a-z"
	else
		echo "WebUI page not found"
	fi
}
### ###

Get_WebUI_Page(){
	MyPage="none"
	for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
		page="/www/user/user$i.asp"
		if [ -f "$page" ] && [ "$(md5sum < "$1")" = "$(md5sum < "$page")" ]; then
			MyPage="user$i.asp"
			return
		elif [ "$MyPage" = "none" ] && [ ! -f "$page" ]; then
			MyPage="user$i.asp"
		fi
	done
}

### locking mechanism code credit to Martineau (@MartineauUK) ###
Mount_WebUI(){
	Print_Output true "Mounting WebUI tab for $SCRIPT_NAME" "$PASS"
	LOCKFILE=/tmp/addonwebui.lock
	FD=386
	eval exec "$FD>$LOCKFILE"
	flock -x "$FD"
	Get_WebUI_Page "$SCRIPT_DIR/YazFi_www.asp"
	if [ "$MyPage" = "none" ]; then
		Print_Output true "Unable to mount $SCRIPT_NAME WebUI page, exiting" "$CRIT"
		flock -u "$FD"
		return 1
	fi
	cp -f "$SCRIPT_DIR/YazFi_www.asp" "$SCRIPT_WEBPAGE_DIR/$MyPage"
	echo "$SCRIPT_NAME" > "$SCRIPT_WEBPAGE_DIR/$(echo $MyPage | cut -f1 -d'.').title"

	if [ "$(uname -o)" = "ASUSWRT-Merlin" ]; then
		if [ ! -f "/tmp/menuTree.js" ]; then
			cp -f "/www/require/modules/menuTree.js" "/tmp/"
		fi

		sed -i "\\~$MyPage~d" /tmp/menuTree.js

		sed -i "/url: \"Guest_network.asp\", tabName:/a {url: \"$MyPage\", tabName: \"$SCRIPT_NAME\"}," /tmp/menuTree.js

		umount /www/require/modules/menuTree.js 2>/dev/null
		mount -o bind /tmp/menuTree.js /www/require/modules/menuTree.js
	fi

	flock -u "$FD"
	Print_Output true "Mounted $SCRIPT_NAME WebUI page as $MyPage" "$PASS"
}

Conf_Download(){
	mkdir -p "/jffs/addons/$SCRIPT_NAME.d"
	/usr/sbin/curl -fsL --retry 3 "$SCRIPT_REPO/files/$SCRIPT_NAME.config.example" -o "$1"
	chmod 0644 "$1"
	dos2unix "$1"
	sleep 1
	Clear_Lock
}

##-------------------------------------##
## Added by Martinski W. [2022-Dec-26] ##
##-------------------------------------##
Conf_ADD_Download()
{
	config_ADD="${1}.ADD.txt"
	/usr/sbin/curl -fsL --retry 3 "$SCRIPT_REPO/files/${SCRIPT_NAME}.config.ADD.txt" -o "$config_ADD"
	chmod 0644 "$config_ADD"
	dos2unix "$config_ADD"
	[ -f "$config_ADD" ] && return 0 || return 1
}

Conf_Exists(){
	if [ -f "$SCRIPT_CONF" ]; then
		dos2unix "$SCRIPT_CONF"
		chmod 0644 "$SCRIPT_CONF"

		##-------------------------------------##
		## Added by Martinski W. [2023-Feb-26] ##
		##-------------------------------------##
		Update_File "$SCRIPT_CONF"

		if [ ! -f "$SCRIPT_CONF.bak" ]; then
			cp -a "$SCRIPT_CONF" "$SCRIPT_CONF.bak"
		fi
		sed -i -e 's/_LANACCESS/_TWOWAYTOGUEST/g' "$SCRIPT_CONF"
		if ! grep -q "_ONEWAYTOGUEST" "$SCRIPT_CONF" ; then
			for CONFIFACE in $IFACELIST_FULL ; do
				CONFIFACETMP="$(Get_Iface_Var "$CONFIFACE")"
				sed -i "/^${CONFIFACETMP}_TWOWAYTOGUEST=/a ${CONFIFACETMP}_ONEWAYTOGUEST=" "$SCRIPT_CONF"
			done
		fi
		if ! grep -q "_ALLOWINTERNET" "$SCRIPT_CONF" ; then
			for CONFIFACE in $IFACELIST_FULL ; do
				CONFIFACETMP="$(Get_Iface_Var "$CONFIFACE")"
				sed -i "/^${CONFIFACETMP}_FORCEDNS=/a ${CONFIFACETMP}_ALLOWINTERNET=true" "$SCRIPT_CONF"
			done
		fi
		sed -i -e 's/"//g' "$SCRIPT_CONF"
		. "$SCRIPT_CONF"
		return 0
	else
		return 1
	fi
}

Firewall_Chains(){
	FWRDSTART="$(iptables -nvL FORWARD --line | grep -E "all.*state RELATED,ESTABLISHED" | tail -1 | awk '{print $1}')"

	case $1 in
		create)
			for CHAIN in $CHAINS; do
				if ! iptables -n -L "$CHAIN" >/dev/null 2>&1; then
					iptables -N "$CHAIN"
					case $CHAIN in
						"$INPT")
							iptables -I INPUT -j "$CHAIN"
						;;
						"$FWRD")
							iptables -I FORWARD "$FWRDSTART" -j "$CHAIN"
						;;
						"$LGRJT")
							iptables -I "$LGRJT" -j REJECT

							if [ -f "$SCRIPT_DIR/.rejectlogging" ]; then
								iptables -I $LGRJT -j LOG --log-prefix "REJECT " --log-tcp-sequence --log-tcp-options --log-ip-options
							fi
						;;
						"$DNSFLTR_DOT")
							iptables -I FORWARD "$FWRDSTART" -p tcp -m tcp --dport 853 -j "$CHAIN"
						;;
					esac
				fi
			done
			for CHAIN in $NATCHAINS; do
				if ! iptables -t nat -n -L "$CHAIN" >/dev/null 2>&1; then
					iptables -t nat -N "$CHAIN"
					case $CHAIN in
						"$DNSFLTR")
							### DNSFilter rules - credit to @RMerlin for the original implementation in Asuswrt ###
							iptables -t nat -I PREROUTING -p udp -m udp --dport 53 -j "$CHAIN"
							iptables -t nat -I PREROUTING -p tcp -m tcp --dport 53 -j "$CHAIN"
							###
						;;
					esac
				fi
			done
		;;
		deleteall)
			for CHAIN in $CHAINS; do
				if iptables -n -L "$CHAIN" >/dev/null 2>&1; then
					case $CHAIN in
						"$INPT")
							iptables -D INPUT -j "$CHAIN"
						;;
						"$FWRD")
							iptables -D FORWARD -j "$CHAIN"
						;;
						"$LGRJT")
							iptables -D "$LGRJT" -j REJECT
						;;
						"$DNSFLTR_DOT")
							iptables -D FORWARD -p tcp -m tcp --dport 853 -j "$CHAIN"
						;;
					esac

					iptables -F "$CHAIN"
					iptables -X "$CHAIN"
				fi
			done
			for CHAIN in $NATCHAINS; do
				if ! iptables -t nat -n -L "$CHAIN" >/dev/null 2>&1; then
					case $CHAIN in
						"$DNSFLTR")
							iptables -t nat -D PREROUTING -p udp -m udp --dport 53 -j "$CHAIN"
							iptables -t nat -D PREROUTING -p tcp -m tcp --dport 53 -j "$CHAIN"
						;;
					esac

					iptables -F "$CHAIN"
					iptables -X "$CHAIN"
				fi
			done
		;;
	esac
}

Firewall_Rules(){
	ACTIONS=""
	IFACE="$2"
	IFACE_WAN=""

	if [ "$(nvram get wan0_proto)" = "pppoe" ] || [ "$(nvram get wan0_proto)" = "pptp" ] || [ "$(nvram get wan0_proto)" = "l2tp" ]; then
		IFACE_WAN="ppp0"
	else
		IFACE_WAN="$(nvram get wan0_ifname)"
	fi

	case $1 in
		create)
			ACTIONS="-D -I"
		;;
		delete)
			ACTIONS="-D"
		;;
	esac

	for ACTION in $ACTIONS; do

		ebtables -t broute "$ACTION" BROUTING -p ipv4 -i "$IFACE" -j DROP
		ebtables -t broute "$ACTION" BROUTING -p ipv6 -i "$IFACE" -j DROP
		ebtables -t broute "$ACTION" BROUTING -p arp -i "$IFACE" -j DROP

		ebtables -t broute -D BROUTING -p IPv4 -i "$IFACE" --ip-dst "$LAN"/24 --ip-proto tcp -j DROP
		ebtables -t broute -D BROUTING -p IPv4 -i "$IFACE" --ip-dst "$LAN" --ip-proto icmp -j ACCEPT
		ebtables -t broute -D BROUTING -p IPv4 -i "$IFACE" --ip-dst "$LAN"/24 --ip-proto icmp -j DROP

		iptables "$ACTION" "$FWRD" -i "$IFACE" -j ACCEPT

		if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_TWOWAYTOGUEST")" = "false" ]; then
			iptables "$ACTION" "$FWRD" ! -i "$IFACE_WAN" -o "$IFACE" -j "$LGRJT"
			iptables "$ACTION" "$FWRD" -i "$IFACE" ! -o "$IFACE_WAN" -j "$LGRJT"
		else
			iptables -D "$FWRD" ! -i "$IFACE_WAN" -o "$IFACE" -j "$LGRJT"
			iptables -D "$FWRD" -i "$IFACE" ! -o "$IFACE_WAN" -j "$LGRJT"
		fi

		if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_ONEWAYTOGUEST")" = "true" ]; then
			iptables "$ACTION" "$FWRD" ! -i "$IFACE_WAN" -o "$IFACE" -j ACCEPT
			iptables "$ACTION" "$FWRD" -i wl+ -o "$IFACE" -j "$LGRJT"
			iptables "$ACTION" "$FWRD" -i "$IFACE" ! -o "$IFACE_WAN" -m state --state RELATED,ESTABLISHED -j ACCEPT
		else
			iptables -D "$FWRD" ! -i "$IFACE_WAN" -o "$IFACE" -j ACCEPT
			iptables -D "$FWRD" -i wl+ -o "$IFACE" -j "$LGRJT"
			iptables -D "$FWRD" -i "$IFACE" ! -o "$IFACE_WAN" -m state --state RELATED,ESTABLISHED -j ACCEPT
		fi

		if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_TWOWAYTOGUEST")" = "false" ] && [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_ONEWAYTOGUEST")" = "true" ]; then
			iptables -D "$FWRD" ! -i "$IFACE_WAN" -o "$IFACE" -j "$LGRJT"
		fi

		iptables "$ACTION" "$INPT" -i "$IFACE" -j "$LGRJT"
		iptables "$ACTION" "$INPT" -i "$IFACE" -p icmp -j ACCEPT
		iptables "$ACTION" "$INPT" -i "$IFACE" -p udp -m multiport --dports 67,123 -j ACCEPT

		ENABLED_WINS="$(nvram get smbd_wins)"
		ENABLED_SAMBA="$(nvram get enable_samba)"
		if ! Validate_Number "" "$ENABLED_SAMBA" silent; then ENABLED_SAMBA=0; fi
		if ! Validate_Number "" "$ENABLED_WINS" silent; then ENABLED_WINS=0; fi

		if [ "$ENABLED_WINS" -eq 1 ] && [ "$ENABLED_SAMBA" -eq 1 ]; then
			iptables "$ACTION" "$INPT" -i "$IFACE" -p udp -m multiport --dports 137,138 -j ACCEPT
		else
			iptables -D "$INPT" -i "$IFACE" -p udp -m multiport --dports 137,138 -j ACCEPT
		fi

		if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_TWOWAYTOGUEST")" = "true" ] || [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_ONEWAYTOGUEST")" = "true" ]; then
			iptables "$ACTION" "$INPT" -i "$IFACE" -d 224.0.0.0/4 -j ACCEPT
		fi

		modprobe xt_comment
		iptables -t nat -D POSTROUTING -s "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_IPADDR" | cut -f1-3 -d".")".0/24 -d "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_IPADDR" | cut -f1-3 -d".")".0/24 -o "$IFACE" -m comment --comment "$(Get_Guest_Name_Old "$IFACE")" -j MASQUERADE
		iptables -t nat "$ACTION" POSTROUTING -s "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_IPADDR" | cut -f1-3 -d".")".0/24 -d "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_IPADDR" | cut -f1-3 -d".")".0/24 -o "$IFACE" -m comment --comment "$(Get_Guest_Name "$IFACE")" -j MASQUERADE

		ENABLED_NTPD=0
		if [ -f /jffs/scripts/nat-start ]; then
			if [ "$(grep -c '# ntpMerlin' /jffs/scripts/nat-start)" -gt 0 ]; then ENABLED_NTPD=1; fi
		fi

		if [ "$ENABLED_NTPD" -eq 1 ]; then
			iptables -t nat "$ACTION" PREROUTING -i "$IFACE" -p udp --dport 123 -j DNAT --to "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_IPADDR" | cut -f1-3 -d".")"."$(echo "$LAN" | cut -f4 -d'.')"
			iptables -t nat "$ACTION" PREROUTING -i "$IFACE" -p tcp --dport 123 -j DNAT --to "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_IPADDR" | cut -f1-3 -d".")"."$(echo "$LAN" | cut -f4 -d'.')"
			iptables "$ACTION" "$FWRD" -i "$IFACE" -p tcp --dport 123 -j REJECT
			iptables "$ACTION" "$FWRD" -i "$IFACE" -p udp --dport 123 -j REJECT
			ip6tables "$ACTION" FORWARD -i "$IFACE" -p tcp --dport 123 -j REJECT
			ip6tables "$ACTION" FORWARD -i "$IFACE" -p udp --dport 123 -j REJECT
		else
			iptables -t nat -D PREROUTING -i "$IFACE" -p udp --dport 123 -j DNAT --to "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_IPADDR" | cut -f1-3 -d".")"."$(echo "$LAN" | cut -f4 -d'.')"
			iptables -t nat -D PREROUTING -i "$IFACE" -p tcp --dport 123 -j DNAT --to "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_IPADDR" | cut -f1-3 -d".")"."$(echo "$LAN" | cut -f4 -d'.')"
			iptables -D "$FWRD" -i "$IFACE" -p tcp --dport 123 -j REJECT
			iptables -D "$FWRD" -i "$IFACE" -p udp --dport 123 -j REJECT
			ip6tables -D FORWARD -i "$IFACE" -p tcp --dport 123 -j REJECT
			ip6tables -D FORWARD -i "$IFACE" -p udp --dport 123 -j REJECT
		fi
	done
}

Firewall_BlockInternet(){
	ACTIONS=""
	IFACE="$2"

	if [ "$(nvram get wan0_proto)" = "pppoe" ] || [ "$(nvram get wan0_proto)" = "pptp" ] || [ "$(nvram get wan0_proto)" = "l2tp" ]; then
		IFACE_WAN="ppp0"
	else
		IFACE_WAN="$(nvram get wan0_ifname)"
	fi

	case $1 in
		create)
			ACTIONS="-D -I"
		;;
		delete)
			ACTIONS="-D"
		;;
	esac

	modprobe xt_comment
	for ACTION in $ACTIONS; do
		iptables "$ACTION" "$FWRD" -i "$IFACE" -o "$IFACE_WAN" -m comment --comment "$(Get_Guest_Name "$IFACE") block internet" -j "$LGRJT"
		iptables "$ACTION" "$FWRD" -i "$IFACE" -o tun1+ -m comment --comment "$(Get_Guest_Name "$IFACE") block internet" -j "$LGRJT"
		iptables "$ACTION" "$FWRD" -i "$IFACE_WAN" -o "$IFACE" -m comment --comment "$(Get_Guest_Name "$IFACE") block internet" -j DROP
		iptables "$ACTION" "$FWRD" -i tun1+ -o "$IFACE" -m comment --comment "$(Get_Guest_Name "$IFACE") block internet" -j DROP
	done
}

Firewall_DNS(){
	ACTIONS=""
	IFACE="$2"

	case $1 in
		create)
			ACTIONS="-D -I"
		;;
		delete)
			ACTIONS="-D"
		;;
	esac

	for ACTION in $ACTIONS; do
		if IP_Local "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS1")" || IP_Local "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS2")"; then
			RULES=$(iptables -nvL "$INPT" --line-number | grep "$IFACE" | grep "pt:53" | awk '{print $1}' | sort -nr)
			for RULENO in $RULES; do
				iptables -D "$INPT" "$RULENO"
			done

			RULES=$(iptables -nvL "$FWRD" --line-number | grep "$IFACE" | grep "pt:53" | awk '{print $1}' | sort -nr)
			for RULENO in $RULES; do
				iptables -D "$FWRD" "$RULENO"
			done

			if IP_Router "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS1")" "$IFACE" || IP_Router "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS2")" "$IFACE"; then
				if ifconfig "br0:pixelserv-tls" | grep -q "inet addr:" >/dev/null 2>&1; then
					IP_PXLSRV=$(ifconfig br0:pixelserv-tls | grep "inet addr:" | cut -d: -f2 | awk '{print $1}')
					iptables "$ACTION" "$INPT" -i "$IFACE" -d "$IP_PXLSRV" -p tcp -m multiport --dports 80,443 -j ACCEPT
				else
					RULES=$(iptables -nvL "$INPT" --line-number | grep "$IFACE" | grep "multiport dports 80,443" | awk '{print $1}' | sort -nr)
					for RULENO in $RULES; do
						iptables -D "$INPT" "$RULENO"
					done
				fi

				for PROTO in tcp udp; do
					iptables "$ACTION" "$INPT" -i "$IFACE" -p "$PROTO" --dport 53 -j ACCEPT
				done
			fi
			if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS1")" != "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS2")" ]; then
				if IP_Local "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS1")" && ! IP_Router "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS1")" "$IFACE"; then
					for PROTO in tcp udp; do
						iptables "$ACTION" "$FWRD" -i "$IFACE" -d "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS1")" -p "$PROTO" --dport 53 -j ACCEPT
						iptables "$ACTION" "$FWRD" -o "$IFACE" -s "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS1")" -p "$PROTO" --sport 53 -j ACCEPT
					done
				fi
				if IP_Local "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS2")" && ! IP_Router "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS2")" "$IFACE"; then
					for PROTO in tcp udp; do
						iptables "$ACTION" "$FWRD" -i "$IFACE" -d "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS2")" -p "$PROTO" --dport 53 -j ACCEPT
						iptables "$ACTION" "$FWRD" -o "$IFACE" -s "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS2")" -p "$PROTO" --sport 53 -j ACCEPT
					done
				fi
			else
				if ! IP_Router "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS1")" "$IFACE"; then
					for PROTO in tcp udp; do
						iptables "$ACTION" "$FWRD" -i "$IFACE" -d "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS1")" -p "$PROTO" --dport 53 -j ACCEPT
						iptables "$ACTION" "$FWRD" -o "$IFACE" -s "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS1")" -p "$PROTO" --sport 53 -j ACCEPT
					done
				fi
			fi
		else
			RULES=$(iptables -nvL "$INPT" --line-number | grep "$IFACE" | grep "pt:53" | awk '{print $1}' | sort -nr)
			for RULENO in $RULES; do
				iptables -D "$INPT" "$RULENO"
			done

			RULES=$(iptables -nvL "$FWRD" --line-number | grep "$IFACE" | grep "pt:53" | awk '{print $1}' | sort -nr)
			for RULENO in $RULES; do
				iptables -D "$FWRD" "$RULENO"
			done
		fi

		### DNSFilter rules - credit to @RMerlin for the original implementation in Asuswrt ###
		if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_FORCEDNS")" = "true" ]; then
			RULES=$(iptables -t nat -nvL "$DNSFLTR" --line-number | grep "$IFACE" | awk '{print $1}' | sort -nr)
			for RULENO in $RULES; do
				iptables -t nat -D "$DNSFLTR" "$RULENO"
			done

			RULES=$(iptables -nvL $DNSFLTR_DOT --line-number | grep "$IFACE" | awk '{print $1}' | sort -nr)
			for RULENO in $RULES; do
				iptables -t nat -D "$DNSFLTR_DOT" "$RULENO"
			done

			iptables -t nat "$ACTION" "$DNSFLTR" -i "$IFACE" -j DNAT --to-destination "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS1")"
			iptables "$ACTION" "$DNSFLTR_DOT" -i "$IFACE" ! -d "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_DNS1")" -j "$LGRJT"
		else
			RULES=$(iptables -t nat -nvL "$DNSFLTR" --line-number | grep "$IFACE" | awk '{print $1}' | sort -nr)
			for RULENO in $RULES; do
				iptables -t nat -D "$DNSFLTR" "$RULENO"
			done

			RULES=$(iptables -nvL $DNSFLTR_DOT --line-number | grep "$IFACE" | awk '{print $1}' | sort -nr)
			for RULENO in $RULES; do
				iptables -t nat -D "$DNSFLTR_DOT" "$RULENO"
			done
		fi
		###
	done
}

Firewall_NVRAM(){
	case $1 in
		create)
			nvram set "${2}_ap_isolate"="1"
		;;
		delete)
			nvram set "${2}_ap_isolate"="0"
		;;
		deleteall)
			for IFACE in $IFACELIST; do
				Firewall_NVRAM delete "$IFACE" 2>/dev/null
			done
		;;
	esac
}

Firewall_NAT(){
	IFACE_WAN=""

	if [ "$(nvram get wan0_proto)" = "pppoe" ] || [ "$(nvram get wan0_proto)" = "pptp" ] || [ "$(nvram get wan0_proto)" = "l2tp" ]; then
		IFACE_WAN="ppp0"
	else
		IFACE_WAN="$(nvram get wan0_ifname)"
	fi

	case $1 in
		create)
			for ACTION in -D -I; do
				modprobe xt_comment
				iptables -t nat -D POSTROUTING -s "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".")".0/24 -o tun1"$3" -m comment --comment "$(Get_Guest_Name_Old "$2") VPN" -j MASQUERADE
				iptables -t nat "$ACTION" POSTROUTING -s "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".")".0/24 -o tun1"$3" -m comment --comment "$(Get_Guest_Name "$2") VPN" -j MASQUERADE
				iptables "$ACTION" "$FWRD" -i "$2" -o "$IFACE_WAN" -j "$LGRJT"
				iptables "$ACTION" "$FWRD" -i "$IFACE_WAN" -o "$2" -j "$LGRJT"
				iptables "$ACTION" "$FWRD" -i "$2" -o tun1"$3" -j ACCEPT
				iptables "$ACTION" "$FWRD" -i tun1"$3" -o "$2" -j ACCEPT
			done
		;;
		delete)
			RULES=$(iptables -t nat -nvL POSTROUTING --line-number | grep "$(Get_Guest_Name_Old "$2") VPN" | awk '{print $1}' | sort -nr)
			for RULENO in $RULES; do
				iptables -t nat -D POSTROUTING "$RULENO"
			done

			RULES=$(iptables -t nat -nvL POSTROUTING --line-number | grep "$(Get_Guest_Name "$2") VPN" | awk '{print $1}' | sort -nr)
			for RULENO in $RULES; do
				iptables -t nat -D POSTROUTING "$RULENO"
			done

			RULES=$(iptables -nvL "$FWRD" --line-number | grep "$2" | grep "tun1" | awk '{print $1}' | sort -nr)
			for RULENO in $RULES; do
				iptables -D "$FWRD" "$RULENO"
			done

			iptables -D "$FWRD" -i "$2" -o "$IFACE_WAN" -j "$LGRJT"
			iptables -D "$FWRD" -i "$IFACE_WAN" -o "$2" -j "$LGRJT"
		;;
		deleteall)
			for IFACE in $IFACELIST; do
				Firewall_NAT delete "$IFACE" 2>/dev/null
			done
		;;
	esac
}

Routing_RPDB(){
	case $1 in
		create)
			if ! ip route show | grep -q "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR")"; then
				ip route del "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".")".0/24 dev "$2" proto kernel src "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").$(nvram get lan_ipaddr | cut -f4 -d".")"
				ip route add "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".")".0/24 dev "$2" proto kernel src "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").$(nvram get lan_ipaddr | cut -f4 -d".")"
			fi
			COUNTER=1
			until [ $COUNTER -gt 5 ]; do
				if ifconfig "tun1$COUNTER" >/dev/null 2>&1; then
					if ! ip route show table ovpnc"$COUNTER" | grep -q "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR")"; then
						ip route del "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".")".0/24 dev "$2" proto kernel table ovpnc"$COUNTER" src "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").$(nvram get lan_ipaddr | cut -f4 -d".")"
						ip route add "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".")".0/24 dev "$2" proto kernel table ovpnc"$COUNTER" src "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").$(nvram get lan_ipaddr | cut -f4 -d".")"
					fi
				fi
				COUNTER=$((COUNTER+1))
			done
		;;
		delete)
			COUNTER=1
			until [ $COUNTER -gt 5 ]; do
				if ifconfig "tun1$COUNTER" >/dev/null 2>&1; then
					ip route del "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".")".0/24 dev "$2" proto kernel table ovpnc"$COUNTER" src "$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").$(nvram get lan_ipaddr | cut -f4 -d".")"
				fi
				COUNTER=$((COUNTER+1))
			done
		;;
	esac

	ip route flush cache
}

Routing_VPNDirector(){
	case $1 in
		initialise)
			VPN_IP_LIST_ORIG=$(cat /jffs/openvpn/vpndirector_rulelist)
			VPN_IP_LIST_NEW="$VPN_IP_LIST_ORIG"
		;;
		create)
			VPN_IFACE_NVRAM=""
			VPN_IFACE_NVRAM="<1>$(Get_Guest_Name "$2")>$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").0/24>>OVPN$3"

			if ! echo "$VPN_IP_LIST_ORIG" | grep -q "$VPN_IFACE_NVRAM"; then
				Routing_VPNDirector delete "$2"
				VPN_IP_LIST_NEW="${VPN_IP_LIST_NEW}${VPN_IFACE_NVRAM}"
			fi

			VPN_IFACE_NVRAM_SAFE="$(echo "<0>$(Get_Guest_Name "$2")>$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").0/24>>OVPN$3" | sed -e 's/\//\\\//g;s/\./\\./g;s/ /\\ /g')"
			VPN_IP_LIST_NEW=$(echo "$VPN_IP_LIST_NEW" | sed -e "s/$VPN_IFACE_NVRAM_SAFE//g")

			COUNTER=1
			until [ $COUNTER -gt 5 ]; do
				if [ $COUNTER -eq "$3" ]; then
					COUNTER=$((COUNTER + 1))
					continue
				fi
				VPN_IFACE_NVRAM_SAFE="$(echo "<0>$(Get_Guest_Name "$2")>$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").0/24>>OVPN$COUNTER" | sed -e 's/\//\\\//g;s/\./\\./g;s/ /\\ /g')"
				VPN_IP_LIST_NEW=$(echo "$VPN_IP_LIST_NEW" | sed -e "s/$VPN_IFACE_NVRAM_SAFE//g")
				VPN_IFACE_NVRAM_SAFE="$(echo "<1>$(Get_Guest_Name "$2")>$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").0/24>>OVPN$COUNTER" | sed -e 's/\//\\\//g;s/\./\\./g;s/ /\\ /g')"
				VPN_IP_LIST_NEW=$(echo "$VPN_IP_LIST_NEW" | sed -e "s/$VPN_IFACE_NVRAM_SAFE//g")
				COUNTER=$((COUNTER + 1))
			done
		;;
		delete)
			COUNTER=1
			until [ $COUNTER -gt 5 ]; do
				VPN_IP_LIST_NEW=$(echo "$VPN_IP_LIST_NEW" | sed -e "s/$(echo '<0>'"$(Get_Guest_Name "$2")" | sed -e 's/\//\\\//g;s/ /\\ /g')>[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\/24>>OVPN$COUNTER//g")
				VPN_IP_LIST_NEW=$(echo "$VPN_IP_LIST_NEW" | sed -e "s/$(echo '<1>'"$(Get_Guest_Name "$2")" | sed -e 's/\//\\\//g;s/ /\\ /g')>[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\/24>>OVPN$COUNTER//g")
				COUNTER=$((COUNTER + 1))
			done
		;;
		deleteall)
			Routing_VPNDirector initialise 2>/dev/null

			for IFACE in $IFACELIST; do
				Routing_VPNDirector delete "$IFACE" 2>/dev/null
			done

			Routing_VPNDirector save 2>/dev/null
		;;
		save)
			if [ "$VPN_IP_LIST_ORIG" != "$VPN_IP_LIST_NEW" ]; then
				printf "%s" "$VPN_IP_LIST_NEW" > /jffs/openvpn/vpndirector_rulelist

				COUNTER=1
				until [ $COUNTER -gt 5 ]; do
					if ifconfig "tun1$COUNTER" >/dev/null 2>&1; then
						if [ "$(nvram get vpn_client"$COUNTER"_rgw)" -ne 2 ]; then
							nvram set vpn_client"$COUNTER"_rgw=2
							nvram commit
						fi
					fi
					COUNTER=$((COUNTER + 1))
				done

				service restart_vpnrouting0 >/dev/null 2>&1
			fi
		;;
	esac
}

Routing_NVRAM(){
	case $1 in
		initialise)
			COUNTER=1
			until [ $COUNTER -gt 5 ]; do
				eval "VPN_IP_LIST_ORIG_$COUNTER=$(echo "$(nvram get "vpn_client${COUNTER}_clientlist")$(nvram get "vpn_client${COUNTER}_clientlist1")$(nvram get "vpn_client${COUNTER}_clientlist2")$(nvram get "vpn_client${COUNTER}_clientlist3")$(nvram get "vpn_client${COUNTER}_clientlist4")$(nvram get "vpn_client${COUNTER}_clientlist5")" | Escape_Sed)"
				eval "VPN_IP_LIST_NEW_$COUNTER=$(eval echo '$'"VPN_IP_LIST_ORIG_"$COUNTER | Escape_Sed)"
				COUNTER=$((COUNTER + 1))
			done
		;;
		create)
			VPN_NVRAM="$(Get_Guest_Name "$2")"
			VPN_IFACE_NVRAM=""
			if [ "$(Firmware_Version_Check "$(nvram get buildno)")" -lt "$(Firmware_Version_Check 384.18)" ]; then
				VPN_IFACE_NVRAM="<$VPN_NVRAM>$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").0/24>0.0.0.0>VPN"
			else
				VPN_IFACE_NVRAM="<$VPN_NVRAM>$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").0/24>>VPN"
			fi
			VPN_IFACE_NVRAM_SAFE="$(echo "$VPN_IFACE_NVRAM" | sed -e 's/\//\\\//g;s/\./\\./g;s/ /\\ /g')"

			if ! eval echo '$'"VPN_IP_LIST_ORIG_$3" | grep -q "$VPN_IFACE_NVRAM"; then
				Routing_NVRAM delete "$2"
				eval "VPN_IP_LIST_NEW_$3=$(echo "$(eval echo '$'"VPN_IP_LIST_NEW_$3")$VPN_IFACE_NVRAM" | Escape_Sed)"
			fi

			COUNTER=1
			until [ $COUNTER -gt 5 ]; do
				if [ $COUNTER -eq "$3" ]; then
					COUNTER=$((COUNTER + 1))
					continue
				fi
				eval "VPN_IP_LIST_NEW_$COUNTER=$(eval echo '$'"VPN_IP_LIST_NEW_$COUNTER" | sed -e "s/$VPN_IFACE_NVRAM_SAFE//g" | Escape_Sed)"
				COUNTER=$((COUNTER + 1))
			done

			VPN_NVRAM="$(Get_Guest_Name_Old "$2")"
			VPN_IFACE_NVRAM=""
			if [ "$(Firmware_Version_Check "$(nvram get buildno)")" -lt "$(Firmware_Version_Check 384.18)" ]; then
				VPN_IFACE_NVRAM="<$VPN_NVRAM>$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").0/24>0.0.0.0>VPN"
			else
				VPN_IFACE_NVRAM="<$VPN_NVRAM>$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").0/24>>VPN"
			fi
			VPN_IFACE_NVRAM_SAFE="$(echo "$VPN_IFACE_NVRAM" | sed -e 's/\//\\\//g;s/\./\\./g;s/ /\\ /g')"

			COUNTER=1
			until [ $COUNTER -gt 5 ]; do
				eval "VPN_IP_LIST_NEW_$COUNTER=$(eval echo '$'"VPN_IP_LIST_NEW_$COUNTER" | sed -e "s/$VPN_IFACE_NVRAM_SAFE//g" | Escape_Sed)"
				COUNTER=$((COUNTER + 1))
			done
		;;
		delete)
			COUNTER=1
			until [ $COUNTER -gt 5 ]; do
				VPN_NVRAM="$(Get_Guest_Name_Old "$2")"
				eval "VPN_IP_LIST_NEW_$COUNTER=$(echo "$(eval echo '$'"VPN_IP_LIST_NEW_$COUNTER")" | sed -e "s/$(echo '<'$VPN_NVRAM | sed -e 's/\//\\\//g' | sed -e 's/ /\\ /g').*>VPN//g" | Escape_Sed)"
				VPN_NVRAM="$(Get_Guest_Name "$2")"
				eval "VPN_IP_LIST_NEW_$COUNTER=$(echo "$(eval echo '$'"VPN_IP_LIST_NEW_$COUNTER")" | sed -e "s/$(echo '<'$VPN_NVRAM | sed -e 's/\//\\\//g' | sed -e 's/ /\\ /g').*>VPN//g" | Escape_Sed)"
				COUNTER=$((COUNTER + 1))
			done
		;;
		deleteall)
			Routing_NVRAM initialise 2>/dev/null

			for IFACE in $IFACELIST; do
				Routing_NVRAM delete "$IFACE" 2>/dev/null
			done

			Routing_NVRAM save 2>/dev/null
		;;
		save)
			COUNTER=1
			until [ $COUNTER -gt 5 ]; do
				if [ "$(eval echo '$'"VPN_IP_LIST_ORIG_$COUNTER")" != "$(eval echo '$'"VPN_IP_LIST_NEW_$COUNTER")" ]; then
					Print_Output true "VPN Client $COUNTER client list has changed, restarting VPN Client $COUNTER"

					if [ "$(/bin/uname -m)" = "aarch64" ]; then
						fullstring="$(eval echo '$'"VPN_IP_LIST_NEW_"$COUNTER)"
						nvram set vpn_client"$COUNTER"_clientlist="$(echo "$fullstring" | cut -c0-255)"
						nvram set vpn_client"$COUNTER"_clientlist1="$(echo "$fullstring" | cut -c256-510)"
						nvram set vpn_client"$COUNTER"_clientlist2="$(echo "$fullstring" | cut -c511-765)"
						nvram set vpn_client"$COUNTER"_clientlist3="$(echo "$fullstring" | cut -c766-1020)"
						nvram set vpn_client"$COUNTER"_clientlist4="$(echo "$fullstring" | cut -c1021-1275)"
						nvram set vpn_client"$COUNTER"_clientlist5="$(echo "$fullstring" | cut -c1276-1530)"
					else
						nvram set vpn_client"$COUNTER"_clientlist="$(eval echo '$'"VPN_IP_LIST_NEW_$COUNTER")"
					fi

					if [ "$(nvram get vpn_client"$COUNTER"_rgw)" -ne 2 ]; then
						nvram set vpn_client"$COUNTER"_rgw=2
					fi
					nvram commit
					service restart_vpnclient$COUNTER >/dev/null 2>&1
				fi
				COUNTER=$((COUNTER + 1))
			done
		;;
	esac
}

DHCP_Conf(){
	case $1 in
		initialise)
			if [ -f /jffs/configs/dnsmasq.conf.add ]; then
				for IFACE in $IFACELIST; do
					BEGIN="### Start of script-generated configuration for interface $IFACE ###"
					END="### End of script-generated configuration for interface $IFACE ###"
					if grep -q "### Start of script-generated configuration for interface $IFACE ###" /jffs/configs/dnsmasq.conf.add; then
						sed -i -e '/'"$BEGIN"'/,/'"$END"'/c\' /jffs/configs/dnsmasq.conf.add
					fi
				done
			fi
			if [ -f "$DNSCONF" ]; then
				cp "$DNSCONF" "$TMPCONF"
			else
				touch "$TMPCONF"
			fi
		;;
		create)
			CONFSTRING=""
			CONFADDSTRING=""

			ENABLED_WINS="$(nvram get smbd_wins)"
			ENABLED_SAMBA="$(nvram get enable_samba)"
			if ! Validate_Number "" "$ENABLED_SAMBA" silent; then ENABLED_SAMBA=0; fi
			if ! Validate_Number "" "$ENABLED_WINS" silent; then ENABLED_WINS=0; fi

			ENABLED_NTPD=0
			if [ -f /jffs/scripts/nat-start ]; then
				if [ "$(grep -c '# ntpMerlin' /jffs/scripts/nat-start)" -gt 0 ]; then ENABLED_NTPD=1; fi
			fi

			if [ "$ENABLED_WINS" -eq 1 ] && [ "$ENABLED_SAMBA" -eq 1 ]; then
				CONFADDSTRING="$CONFADDSTRING||||dhcp-option=$2,44,$(nvram get lan_ipaddr)"
			fi

			if [ "$ENABLED_NTPD" -eq 1 ]; then
				CONFADDSTRING="$CONFADDSTRING||||dhcp-option=$2,42,$(nvram get lan_ipaddr)"
			fi

			##----------------------------------------------##
			## Added/modified by Martinski W. [2023-Jan-24] ##
			##----------------------------------------------##
			DHCP_LEASE_VAL="$(eval echo '$'"$(Get_Iface_Var "$2")_DHCPLEASE")"
			if ! Validate_DHCP_LeaseTime "$(Get_Iface_Var "$2")_DHCPLEASE" "$DHCP_LEASE_VAL"
			then
				DHCP_LEASE_VAL="$(nvram get dhcp_lease)"
				if ! Validate_Number "" "$DHCP_LEASE_VAL" silent ; then DHCP_LEASE_VAL=86400 ; fi
			fi
			if [ "$DHCP_LEASE_VAL" = "0" ] || [ "$DHCP_LEASE_VAL" = "$InfiniteLeaseTimeTag" ]
			then DHCP_LEASE_VAL="$InfiniteLeaseTimeVal"
			elif echo "$DHCP_LEASE_VAL" | grep -q "^[0-9]\{1,7\}$"
			then DHCP_LEASE_VAL="${DHCP_LEASE_VAL}s" ; fi

			##----------------------------------------##
			## Modified by Martinski W. [2022-Apr-07] ##
			##----------------------------------------##
			CONFSTRING="interface=$2||||dhcp-range=$2,$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").$(eval echo '$'"$(Get_Iface_Var "$2")_DHCPSTART"),$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").$(eval echo '$'"$(Get_Iface_Var "$2")_DHCPEND"),255.255.255.0,${DHCP_LEASE_VAL}||||dhcp-option=$2,3,$(eval echo '$'"$(Get_Iface_Var "$2")_IPADDR" | cut -f1-3 -d".").$(nvram get lan_ipaddr | cut -f4 -d".")||||dhcp-option=$2,6,$(eval echo '$'"$(Get_Iface_Var "$2")_DNS1"),$(eval echo '$'"$(Get_Iface_Var "$2")_DNS2")$CONFADDSTRING"

			BEGIN="### Start of script-generated configuration for interface $2 ###"
			END="### End of script-generated configuration for interface $2 ###"
			if grep -q "### Start of script-generated configuration for interface $2 ###" "$TMPCONF"; then
				sed -i -e '/'"$BEGIN"'/,/'"$END"'/c\'"$BEGIN"'||||'"$CONFSTRING"'||||'"$END" "$TMPCONF"
			else
				printf "\\n%s\\n%s\\n%s\\n" "$BEGIN" "$CONFSTRING" "$END" >> "$TMPCONF"
			fi
		;;
		delete)
			BEGIN="### Start of script-generated configuration for interface $2 ###"
			END="### End of script-generated configuration for interface $2 ###"
			if grep -q "### Start of script-generated configuration for interface $2 ###" "$TMPCONF"; then
				sed -i -e '/'"$BEGIN"'/,/'"$END"'/c\' "$TMPCONF"
			fi
		;;
		deleteall)
			DHCP_Conf initialise 2>/dev/null
			for IFACE in $IFACELIST; do
				BEGIN="### Start of script-generated configuration for interface $IFACE ###"
				END="### End of script-generated configuration for interface $IFACE ###"
				if grep -q "### Start of script-generated configuration for interface $IFACE ###" "$TMPCONF"; then
					sed -i -e '/'"$BEGIN"'/,/'"$END"'/c\' "$TMPCONF"
				fi
			done

			DHCP_Conf save 2>/dev/null
		;;
		save)
			sed -i -e 's/||||/\n/g' "$TMPCONF"

			if ! diff -q "$DNSCONF" "$TMPCONF" >/dev/null 2>&1; then
				cp "$TMPCONF" "$DNSCONF"
				service restart_dnsmasq >/dev/null 2>&1
				Print_Output true "DHCP configuration updated"
				sleep 2
			fi

			rm -f "$TMPCONF"
		;;
	esac
}

Config_Networks(){
	Print_Output true "$SCRIPT_NAME $SCRIPT_VERSION starting up"
	WIRELESSRESTART="false"
	GUESTLANENABLED="false"

	Create_Dirs
	Create_Symlinks

	Auto_Startup create 2>/dev/null
	Auto_Cron create 2>/dev/null
	Auto_DNSMASQ create 2>/dev/null
	Auto_ServiceEvent create 2>/dev/null
	Auto_ServiceStart create 2>/dev/null
	Auto_OpenVPNEvent create 2>/dev/null

	if ! Conf_Exists; then
		Conf_Download "$SCRIPT_CONF"
		Clear_Lock
		return 1
	fi

	if ! Conf_Validate; then
		Clear_Lock
		return 1
	fi

	. $SCRIPT_CONF

	DHCP_Conf initialise 2>/dev/null

	if [ "$(Firmware_Version_Check "$(nvram get buildno)")" -lt "$(Firmware_Version_Check 386.3)" ]; then
		Routing_NVRAM initialise 2>/dev/null
	else
		Routing_VPNDirector initialise 2>/dev/null
	fi

	Firewall_Chains create 2>/dev/null
	iptables -F "$DNSFLTR_DOT" 2>/dev/null

	for IFACE in $IFACELIST; do
		VPNCLIENTNO=$(eval echo '$'"$(Get_Iface_Var "$IFACE")_VPNCLIENTNUMBER")

		if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_ENABLED")" = "true" ]; then
			Iface_Manage create "$IFACE" 2>/dev/null

			Firewall_Rules create "$IFACE" 2>/dev/null

			if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_ALLOWINTERNET")" = "true" ]; then
				if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_REDIRECTALLTOVPN")" = "true" ]; then
					Print_Output true "$IFACE (SSID: $(nvram get "${IFACE}_ssid")) - VPN redirection enabled, sending all interface internet traffic over VPN Client $VPNCLIENTNO"

					if [ "$(Firmware_Version_Check "$(nvram get buildno)")" -lt "$(Firmware_Version_Check 386.3)" ]; then
						Routing_NVRAM create "$IFACE" "$VPNCLIENTNO" 2>/dev/null
					else
						Routing_VPNDirector create "$IFACE" "$VPNCLIENTNO" 2>/dev/null
					fi

					Firewall_NAT create "$IFACE" "$VPNCLIENTNO" 2>/dev/null
				else
					Print_Output true "$IFACE (SSID: $(nvram get "${IFACE}_ssid")) - sending all interface internet traffic over WAN interface"

					Firewall_NAT delete "$IFACE" 2>/dev/null

					if [ "$(Firmware_Version_Check "$(nvram get buildno)")" -lt "$(Firmware_Version_Check 386.3)" ]; then
						Routing_NVRAM delete "$IFACE" 2>/dev/null
					else
						Routing_VPNDirector delete "$IFACE" 2>/dev/null
					fi
				fi
			else
				Firewall_NAT delete "$IFACE" 2>/dev/null

				if [ "$(Firmware_Version_Check "$(nvram get buildno)")" -lt "$(Firmware_Version_Check 386.3)" ]; then
					Routing_NVRAM delete "$IFACE" 2>/dev/null
				else
					Routing_VPNDirector delete "$IFACE" 2>/dev/null
				fi
			fi

			Firewall_DNS create "$IFACE" 2>/dev/null

			if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_ALLOWINTERNET")" = "false" ]; then
				Print_Output true "$IFACE (SSID: $(nvram get "${IFACE}_ssid")) - allow internet disabled, blocking all interface internet traffic"
				Firewall_BlockInternet create "$IFACE" 2>/dev/null
			else
				Firewall_BlockInternet delete "$IFACE" 2>/dev/null
			fi

			if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_CLIENTISOLATION")" = "true" ]; then
				ISOBEFORE="$(nvram get "${IFACE}_ap_isolate")"
				if ! Validate_Number "" "$ISOBEFORE" silent; then ISOBEFORE=0; fi
				Firewall_NVRAM create "$IFACE" 2>/dev/null
				ISOAFTER="$(nvram get "${IFACE}_ap_isolate")"
				if ! Validate_Number "" "$ISOAFTER" silent; then ISOAFTER=0; fi
				if [ "$ISOBEFORE" -ne "$ISOAFTER" ]; then
					WIRELESSRESTART="true"
				fi
			else
				ISOBEFORE="$(nvram get "${IFACE}_ap_isolate")"
				if ! Validate_Number "" "$ISOBEFORE" silent; then ISOBEFORE=0; fi
				Firewall_NVRAM delete "$IFACE" 2>/dev/null
				ISOAFTER="$(nvram get "${IFACE}_ap_isolate")"
				if ! Validate_Number "" "$ISOAFTER" silent; then ISOAFTER=0; fi
				if [ "$ISOBEFORE" -ne "$ISOAFTER" ]; then
					WIRELESSRESTART="true"
				fi
			fi

			if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_ONEWAYTOGUEST")" = "true" ] || [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_TWOWAYTOGUEST")" = "true" ]; then
				GUESTLANENABLED="true"
			fi

			#Set guest interface LAN access to allowed in f/w, prevent creating VLAN
			if [ "$(nvram get "${IFACE}_lanaccess")" != "on" ]; then
				nvram set "$IFACE"_lanaccess=on
				WIRELESSRESTART="true"
			fi

			Routing_RPDB create "$IFACE" 2>/dev/null

			DHCP_Conf create "$IFACE" 2>/dev/null

			sleep 1
		else
			#Remove firewall rules for guest interface
			Firewall_Rules delete "$IFACE" 2>/dev/null

			Firewall_DNS delete "$IFACE" 2>/dev/null

			Firewall_BlockInternet delete "$IFACE" 2>/dev/null

			#Reset guest interface ISOLATION
			ISOBEFORE="$(nvram get "${IFACE}_ap_isolate")"
			if ! Validate_Number "" "$ISOBEFORE" silent; then ISOBEFORE=0; fi
			Firewall_NVRAM delete "$IFACE" 2>/dev/null
			ISOAFTER="$(nvram get "${IFACE}_ap_isolate")"
			if ! Validate_Number "" "$ISOAFTER" silent; then ISOAFTER=0; fi
			if [ "$ISOBEFORE" -ne "$ISOAFTER" ]; then
				WIRELESSRESTART="true"
			fi

			Iface_Manage delete "$IFACE" 2>/dev/null

			DHCP_Conf delete "$IFACE" 2>/dev/null

			if [ "$(Firmware_Version_Check "$(nvram get buildno)")" -lt "$(Firmware_Version_Check 386.3)" ]; then
				Routing_NVRAM delete "$IFACE" 2>/dev/null
			else
				Routing_VPNDirector delete "$IFACE" 2>/dev/null
			fi

			Routing_RPDB delete "$IFACE" 2>/dev/null

			Firewall_NAT delete "$IFACE" 2>/dev/null
		fi
	done

	if [ "$(Firmware_Version_Check "$(nvram get buildno)")" -lt "$(Firmware_Version_Check 386.3)" ]; then
		Routing_NVRAM save 2>/dev/null
	else
		Routing_VPNDirector save 2>/dev/null
	fi

	DHCP_Conf save 2>/dev/null

	if [ "$GUESTLANENABLED" = "true" ]; then
		Avahi_Conf create
	else
		Avahi_Conf delete
	fi

	if [ "$WIRELESSRESTART" = "true" ]; then
		nvram commit
		Clear_Lock
		service restart_wireless >/dev/null 2>&1
	elif [ "$WIRELESSRESTART" = "false" ]; then
		Execute_UserScripts
		Iface_BounceClients
	fi

	Print_Output true "$SCRIPT_NAME $SCRIPT_VERSION completed successfully" "$PASS"
	Clear_Lock
}

Execute_UserScripts(){
	FILES="$USER_SCRIPT_DIR/*.sh"
	for f in $FILES; do
		if [ -f "$f" ]; then
			Print_Output true "Executing user script: $f"
			sh "$f"
		fi
	done
}

### Code adapted from firmware WebUI function, credit to @RMerlin ###
Generate_QRCode(){
	QRGUEST_WL="$1"
	QRSSID="S:$(nvram get "$QRGUEST_WL"_ssid | sed 's/[\\":;,]/\\$&/g');"
	QRAUTHMODE=$(nvram get "$QRGUEST_WL"_auth_mode_x)

	if [ "$QRAUTHMODE" = 'psk' ] || [ "$QRAUTHMODE" = 'psk2' ] || [ "$QRAUTHMODE" = 'sae' ] || [ "$QRAUTHMODE" = 'pskpsk2' ] || [ "$QRAUTHMODE" = 'psk2sae' ]; then
		QRTYPE="T:WPA;"
		QRPASS="P:$(nvram get "$QRGUEST_WL"_wpa_psk | sed 's/[\\":;,]/\\$&/g');"
	elif [ "$QRAUTHMODE" = "open" ] && [ "$(nvram get "$QRGUEST_WL"_wep_x)" -eq 0 ]; then
		QRTYPE="T:;"
		QRPASS="P:;"
	elif [ "$QRAUTHMODE" = "shared" ] || [ "$QRAUTHMODE" = "open" ]; then
		QRTYPE="T:WEP;"
		QRKEYINDEX=$(nvram get "$QRGUEST_WL"_key)
		QRPASS="$(nvram get "$QRGUEST_WL"_key"$QRKEYINDEX");"
	else
		QRSSID="" # Unsupported
	fi

	if [ "$(nvram get "$QRGUEST_WL"_closed)" -eq 1 ]; then
		QRHIDE="H:true;"
	fi

	if [ "$QRSSID" != "" ]; then
		qrencode -t ANSI -o - "WIFI:${QRTYPE}${QRSSID}${QRPASS}${QRHIDE};"
	else
		printf "\\nQR Code generation not supported for this guest network. Please check configuration.\\n"
	fi
	QRTYPE=""
	QRSSID=""
	QRPASS=""
	QRHIDE=""
}
### ###

Shortcut_Script(){
	case $1 in
		create)
			if [ -d /opt/bin ] && [ ! -f "/opt/bin/$SCRIPT_NAME" ] && [ -f "/jffs/scripts/$SCRIPT_NAME" ]; then
				ln -s "/jffs/scripts/$SCRIPT_NAME" /opt/bin
				chmod 0755 "/opt/bin/$SCRIPT_NAME"
			fi
		;;
		delete)
			if [ -f "/opt/bin/$SCRIPT_NAME" ]; then
				rm -f "/opt/bin/$SCRIPT_NAME"
			fi
		;;
	esac
}

PressEnter(){
	while true; do
		printf "Press enter to continue..."
		read -r key
		case "$key" in
			*)
				break
			;;
		esac
	done
	return 0
}

ScriptHeader(){
	clear
	printf "\\n"
	printf "${BOLD}#############################################${CLEARFORMAT}\\n"
	printf "${BOLD}##                                         ##${CLEARFORMAT}\\n"
	printf "${BOLD}##     __     __          ______  _        ##${CLEARFORMAT}\\n"
	printf "${BOLD}##     \ \   / /         |  ____|(_)       ##${CLEARFORMAT}\\n"
	printf "${BOLD}##      \ \_/ /__ _  ____| |__    _        ##${CLEARFORMAT}\\n"
	printf "${BOLD}##       \   // _  ||_  /|  __|  | |       ##${CLEARFORMAT}\\n"
	printf "${BOLD}##        | || (_| | / / | |     | |       ##${CLEARFORMAT}\\n"
	printf "${BOLD}##        |_| \__,_|/___||_|     |_|       ##${CLEARFORMAT}\\n"
	printf "${BOLD}##                                         ##${CLEARFORMAT}\\n"
	printf "${BOLD}##           %s on %-11s         ##${CLEARFORMAT}\\n" "$SCRIPT_VERSION" "$ROUTER_MODEL"
	printf "${BOLD}##                                         ##${CLEARFORMAT}\\n"
	printf "${BOLD}##    https://github.com/jackyaz/YazFi/    ##${CLEARFORMAT}\\n"
	printf "${BOLD}##                                         ##${CLEARFORMAT}\\n"
	printf "${BOLD}#############################################${CLEARFORMAT}\\n"
	printf "\\n"
}

MainMenu(){
	printf "WebUI for %s is available at:\\n${SETTING}%s${CLEARFORMAT}\\n\\n" "$SCRIPT_NAME" "$(Get_WebUI_URL)"
	printf "1.    Apply %s settings\\n\\n" "$SCRIPT_NAME"
	printf "2.    Show connected clients using %s\\n\\n" "$SCRIPT_NAME"
	printf "3.    Edit %s config\\n" "$SCRIPT_NAME"
	printf "4.    Edit Guest Network config (SSID + passphrase)\\n\\n"
	if [ -f /opt/bin/qrencode ]; then
		printf "5.    Show QR Code for Guest Network\\n\\n"
	else
		printf "\\nQR Code generation not supported.\\n\\n"
	fi
	printf "u.    Check for updates\\n"
	printf "uf.   Update %s with latest version (force update)\\n\\n" "$SCRIPT_NAME"
	printf "d.    Generate %s diagnostics\\n\\n" "$SCRIPT_NAME"
	printf "e.    Exit %s\\n\\n" "$SCRIPT_NAME"
	printf "z.    Uninstall %s\\n" "$SCRIPT_NAME"
	printf "\\n"
	printf "${BOLD}#############################################${CLEARFORMAT}\\n"
	printf "\\n"

	while true; do
		printf "Choose an option:  "
		read -r menu
		case "$menu" in
			1)
				printf "\\n"
				if Check_Lock menu; then
					Config_Networks
					Clear_Lock
				fi
				PressEnter
				break
			;;
			2)
				printf "\\n"
				Menu_Status
				PressEnter
				break
			;;
			3)
				printf "\\n"
				if Check_Lock menu; then
					Menu_Edit
				else
					PressEnter
				fi
				break
			;;
			4)
				printf "\\n"
				if Check_Lock menu; then
					Menu_GuestConfig
				else
					PressEnter
				fi
				break
			;;
			5)
				if [ -f /opt/bin/qrencode ]; then
					Menu_QRCode
					printf "\\n"
					PressEnter
				else
					printf "\\nQR Code generation not supported.\\n\\n"
					PressEnter
				fi
				break
			;;
			u)
				printf "\\n"
				if Check_Lock menu; then
					Update_Version
					Clear_Lock
				fi
				PressEnter
				break
			;;
			uf)
				printf "\\n"
				if Check_Lock menu; then
					Update_Version force
					Clear_Lock
				fi
				PressEnter
				break
			;;
			d)
				ScriptHeader
				Menu_Diagnostics
				PressEnter
				break
			;;
			e)
				ScriptHeader
				printf "\\n${BOLD}Thanks for using %s!${CLEARFORMAT}\\n\\n\\n" "$SCRIPT_NAME"
				exit 0
			;;
			z)
				while true; do
					printf "\\n${BOLD}Are you sure you want to uninstall %s? (y/n)${CLEARFORMAT}  " "$SCRIPT_NAME"
					read -r confirm
					case "$confirm" in
						y|Y)
							Menu_Uninstall
							exit 0
						;;
						*)
							break
						;;
					esac
				done
			;;
			*)
				printf "\\nPlease choose a valid option\\n\\n"
			;;
		esac
	done

	ScriptHeader
	MainMenu
}

Check_Requirements(){
	CHECKSFAILED="false"

	if [ "$(nvram get sw_mode)" -ne 1 ]; then
		Print_Output false "Device is not running in router mode - non-router modes are not supported" "$ERR"
		CHECKSFAILED="true"
	fi

	if [ "$(nvram get jffs2_scripts)" -ne 1 ]; then
		nvram set jffs2_scripts=1
		nvram commit
		Print_Output true "Custom JFFS Scripts enabled" "$WARN"
	fi

	if [ "$(nvram get wl0_radio)" -eq 0 ] && [ "$(nvram get wl1_radio)" -eq 0 ] && [ "$(nvram get wl_radio)" -eq 0 ]; then
		Print_Output false "No wireless radios are enabled!" "$ERR"
		CHECKSFAILED="true"
	fi

	if [ "$(Firmware_Version_Check "$(nvram get buildno)")" -lt "$(Firmware_Version_Check 384.5)" ] && [ "$(Firmware_Version_Check "$(nvram get buildno)")" -ne "$(Firmware_Version_Check 374.43)" ]; then
		Print_Output true "Older Merlin firmware detected - service-event requires 384.5 or later" "$WARN"
		Print_Output true "Please update to benefit from $SCRIPT_NAME detecting wireless restarts" "$WARN"
	elif [ "$(Firmware_Version_Check "$(nvram get buildno)")" -eq "$(Firmware_Version_Check 374.43)" ]; then
		Print_Output true "John's fork detected - service-event requires 374.43_32D6j9527 or later" "$WARN"
		Print_Output true "Please update to benefit from $SCRIPT_NAME detecting wireless restarts" "$WARN"
	fi

	if [ "$CHECKSFAILED" = "false" ]; then
		return 0
	else
		return 1
	fi
}

Menu_Install(){
	ScriptHeader
	Print_Output true "Welcome to $SCRIPT_NAME $SCRIPT_VERSION, a script by JackYaz"
	sleep 1

	Print_Output true "Checking your router meets the requirements for $SCRIPT_NAME"

	if ! Check_Requirements; then
		Print_Output true "Requirements for $SCRIPT_NAME not met, please see above for the reason(s)" "$CRIT"
		PressEnter
		Clear_Lock
		rm -f "/jffs/scripts/$SCRIPT_NAME" 2>/dev/null
		exit 1
	fi

	Create_Dirs
	Create_Symlinks

	if Firmware_Version_WebUI ; then
		Update_File shared-jy.tar.gz
		Update_File YazFi_www.asp
	else
		Print_Output false "WebUI is only support on firmware versions with addon support" "$WARN"
	fi

	if ! Conf_Exists; then
		Conf_Download "$SCRIPT_CONF"
	else
		Print_Output false "Existing $SCRIPT_CONF found. This will be kept by $SCRIPT_NAME"
		Conf_Download "$SCRIPT_CONF.example"
	fi

	Update_File README.md

	Shortcut_Script create
	Set_Version_Custom_Settings local "$SCRIPT_VERSION"
	Set_Version_Custom_Settings server "$SCRIPT_VERSION"
	Auto_Startup create 2>/dev/null
	Auto_Cron create 2>/dev/null
	Auto_DNSMASQ create 2>/dev/null
	Auto_ServiceEvent create 2>/dev/null
	Auto_ServiceStart create 2>/dev/null
	Auto_OpenVPNEvent create 2>/dev/null

	Print_Output false "You can access $SCRIPT_NAME's configuration via the Guest Networks section of the WebUI" "$PASS"
	Print_Output false "Alternatively, use $SCRIPT_NAME's menu via amtm (if installed), with /jffs/scripts/$SCRIPT_NAME or simply $SCRIPT_NAME"
	Clear_Lock
	PressEnter
	Download_File "$SCRIPT_REPO/install-success/LICENSE" "$SCRIPT_DIR/LICENSE"
	ScriptHeader
	MainMenu
}

Menu_Edit(){
	texteditor=""
	exitmenu="false"
	if ! Conf_Exists; then
		Conf_Download "$SCRIPT_CONF"
	fi
	printf "\\n${BOLD}A choice of text editors is available:${CLEARFORMAT}\\n"
	printf "1.    nano (recommended for beginners)\\n"
	printf "2.    vi\\n"
	printf "\\ne.    Exit to main menu\\n"

	while true; do
		printf "\\n${BOLD}Choose an option:${CLEARFORMAT}  "
		read -r editor
		case "$editor" in
			1)
				texteditor="nano -K"
				break
			;;
			2)
				texteditor="vi"
				break
			;;
			e)
				exitmenu="true"
				break
			;;
			*)
				printf "\\nPlease choose a valid option\\n\\n"
			;;
		esac
	done

	if [ "$exitmenu" != "true" ]; then
		$texteditor "$SCRIPT_CONF"
	fi
	Clear_Lock
}

Menu_GuestConfig(){
	exitmenu="false"
	selectediface=""
	changesmade="false"

	ScriptHeader

	printf "\\n${BOLD}Please select a Guest Network:${CLEARFORMAT}\\n\\n"
	COUNTER=1
	for IFACE_MENU in $IFACELIST; do
		if [ $((COUNTER % 4)) -eq 0 ]; then printf "\\n"; fi
		IFACE_MENU_TEST="$(nvram get "${IFACE_MENU}_bss_enabled")"
		if ! Validate_Number "" "$IFACE_MENU_TEST" silent; then IFACE_MENU_TEST=0; fi
		if [ "$IFACE_MENU_TEST" -eq 1 ]; then
			printf "%s.    %s (SSID: %s)\\n" "$COUNTER" "$(Get_Guest_Name "$IFACE_MENU")" "$(nvram get "${IFACE_MENU}_ssid")"
		fi
		COUNTER=$((COUNTER + 1))
	done

	printf "\\ne.    Go back\\n"

	while true; do
		selectediface=""
		printf "\\n${BOLD}Choose an option:${CLEARFORMAT}  "
		read -r selectedguest

		case "$selectedguest" in
			1|2|3|4|5|6|7|8|9)
				selectediface="$(echo "$IFACELIST" | awk '{print $'"$selectedguest"'}')"
			;;
			e)
				exitmenu="true"
				break
			;;
			*)
				printf "\\nPlease choose a valid option\\n\\n"
			;;
		esac

		if [ -n "$selectediface" ]; then
			if ! Validate_Exists_IFACE "$selectediface" silent; then
				printf "\\nSelected guest (%s) not supported on your router, please choose a different option\\n" "$selectediface"
			else
				selectediface_TEST="$(nvram get "${selectediface}_bss_enabled")"
				if ! Validate_Number "" "$selectediface_TEST" silent; then selectediface_TEST=0; fi
				if [ "$selectediface_TEST" -eq 1 ]; then
					break
				else
					printf "\\nSelected guest (%s) not enabled on your router, please choose a different option\\n" "$selectediface"
				fi
			fi
		fi
	done

	if [ "$exitmenu" != "true" ]; then
		while true; do
			ScriptHeader
			printf "\\n${BOLD}%s (%s)${CLEARFORMAT}\\n\\n" "$(Get_Guest_Name "$selectediface")" "$selectediface"
			printf "${BOLD}Available options:${CLEARFORMAT}\\n\\n"
			printf "1.    Set SSID (current: %s)\\n" "$(nvram get "${selectediface}_ssid")"
			printf "2.    Set passphrase (current: %s)\\n" "$(nvram get "${selectediface}_wpa_psk")"
			printf "\\ne.    Go back\\n"
			printf "\\n${BOLD}Choose an option:${CLEARFORMAT}  "
			read -r guestoption
			case "$guestoption" in
				1)
					printf "\\n${BOLD}Please enter your new SSID:${CLEARFORMAT}  "
					read -r newssid
					newssidclean="$newssid"
					if ! Validate_String "$newssid"; then
						newssidclean="$(echo "$newssid" | sed 's/[^a-zA-Z0-9]//g')"
					fi
					nvram set "${selectediface}_ssid"="$newssidclean"
					nvram commit
					changesmade="true"
				;;
				2)
					while true; do
						printf "\\n${BOLD}Available options:${CLEARFORMAT}\\n\\n"
						printf "1.    Generate random passphrase\\n"
						printf "2.    Manually set passphrase\\n"
						printf "\\ne.    Go back\\n"
						printf "\\n${BOLD}Choose an option:${CLEARFORMAT}  "
						read -r passoption
						case "$passoption" in
							1)
								validpasslength=""
								while true; do
									printf "\\n${BOLD}How many characters? (8-32)${CLEARFORMAT}  "
									read -r passlength
									if Validate_Number "" "$passlength" silent; then
										if [ "$passlength" -le 32 ] && [ "$passlength" -ge 8 ]; then
											validpasslength="$passlength"
											break
										else
											printf "\\nPlease choose a number between 8 and 32\\n\\n"
										fi
									elif [ "$passlength" = "e" ]; then
										break
									else
										printf "\\nPlease choose a valid number\\n\\n"
									fi
								done

								if [ -n "$validpasslength" ]; then
									newpassphrase="$(Generate_Random_String "$validpasslength")"
									newpassphraseclean="$(echo "$newpassphrase" | sed 's/[^a-zA-Z0-9]//g')"
									Set_WiFi_Passphrase "$selectediface" "$newpassphraseclean"
									changesmade="true"
									break
								fi
							;;
							2)
								printf "\\n${BOLD}Please enter your new passphrase:${CLEARFORMAT}  "
								read -r newpassphrase
								newpassphraseclean="$newpassphrase"
								if ! Validate_String "$newpassphrase"; then
									newpassphraseclean="$(echo "$newpassphrase" | sed 's/[^a-zA-Z0-9]//g')"
								fi

								Set_WiFi_Passphrase "$selectediface" "$newpassphraseclean"
								changesmade="true"
								break
							;;
							e)
								break
							;;
							*)
								printf "\\nPlease choose a valid option\\n\\n"
							;;
						esac
					done
				;;
				e)
					if [ "$changesmade" = "true" ]; then
						while true; do
							printf "\\n${BOLD}Do you want to restart wireless services now? (y/n)${CLEARFORMAT}  "
							read -r confirmrestart
							case "$confirmrestart" in
								y|Y)
									Clear_Lock
									service restart_wireless >/dev/null 2>&1
									break
								;;
								*)
									break
								;;
							esac
						done
					fi
					ScriptHeader
					break
				;;
				*)
					printf "\\nPlease choose a valid option\\n\\n"
				;;
			esac
		done

		Menu_GuestConfig
	fi
	Clear_Lock
}

Menu_QRCode(){
	exitmenu="false"
	selectediface=""

	ScriptHeader

	printf "\\n${BOLD}Please select a Guest Network:${CLEARFORMAT}\\n\\n"
	COUNTER=1
	for IFACE_MENU in $IFACELIST; do
		if [ $((COUNTER % 4)) -eq 0 ]; then printf "\\n"; fi
		IFACE_MENU_TEST="$(nvram get "${IFACE_MENU}_bss_enabled")"
		if ! Validate_Number "" "$IFACE_MENU_TEST" silent; then IFACE_MENU_TEST=0; fi
		if [ "$IFACE_MENU_TEST" -eq 1 ]; then
			printf "%s.    %s (SSID: %s)\\n" "$COUNTER" "$(Get_Guest_Name "$IFACE_MENU")" "$(nvram get "${IFACE_MENU}_ssid")"
		fi
		COUNTER=$((COUNTER + 1))
	done

	printf "\\ne.    Go back\\n"

	while true; do
		selectediface=""
		printf "\\n${BOLD}Choose an option:${CLEARFORMAT}  "
		read -r selectedguest

		case "$selectedguest" in
			1|2|3|4|5|6|7|8|9)
				selectediface="$(echo "$IFACELIST" | awk '{print $'"$selectedguest"'}')"
			;;
			e)
				exitmenu="true"
				break
			;;
			*)
				printf "\\nPlease choose a valid option\\n\\n"
			;;
		esac

		if [ -n "$selectediface" ]; then
			if ! Validate_Exists_IFACE "$selectediface" silent; then
				printf "\\nSelected guest (%s) not supported on your router, please choose a different option\\n" "$selectediface"
			else
				selectediface_TEST="$(nvram get "${selectediface}_bss_enabled")"
				if ! Validate_Number "" "$selectediface_TEST" silent; then selectediface_TEST=0; fi
				if [ "$selectediface_TEST" -eq 1 ]; then
					break
				else
					printf "\\nSelected guest (%s) not enabled on your router, please choose a different option\\n" "$selectediface"
				fi
			fi
		fi
	done

	if [ "$exitmenu" != "true" ]; then
		if [ -f /opt/bin/qrencode ]; then
			printf "\\n"
			Generate_QRCode "${selectediface}"
		else
			printf "\\nQR Code generation not supported.\\n"
		fi
	fi
}

##----------------------------------------##
## Modified by Martinski W. [2023-Nov-18] ##
##----------------------------------------##
Menu_Status(){
	renice 15 $$
	### This function suggested by @HuskyHerder, code inspired by @ColinTaylor's wireless monitor script ###
	STATUSOUTPUTFILE="$SCRIPT_DIR/.connectedclients"
	rm -f "$STATUSOUTPUTFILE"
	TMPSTATUSOUTPUTFILE="/tmp/.connectedclients"
	. "$SCRIPT_CONF"

	if [ ! -f /opt/bin/dig ] && [ -f /opt/bin/opkg ]; then
		opkg update
		opkg install bind-dig
	fi

	local NoARGs=false
	if [ $# -eq 0 ] || [ -z "$1" ] ; then NoARGs=true ; fi

	"$NoARGs" && ScriptHeader
	"$NoARGs" && printf "${BOLD}$PASS%sQuerying router for connected WiFi clients...${CLEARFORMAT}\\n\\n" ""

	printf "INTERFACE,HOSTNAME,IP,MAC,CONNECTED,RX,TX,RSSI,PHY\\n" >> "$TMPSTATUSOUTPUTFILE"

	##----------------------------------------##
	## Modified by Martinski W. [2023-Sep-11] ##
	##----------------------------------------##
	ARP_CACHE="$(cat /proc/net/arp)"
	NOT_LANIP="grep -vF \"$(nvram get lan_ipaddr | cut -d'.' -f1-3).\""

	for IFACE in $IFACELIST; do
		if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_ENABLED")" = "true" ] && Validate_Exists_IFACE "$IFACE" silent && Validate_Enabled_IFACE "$IFACE" silent; then

			"$NoARGs" && printf "%75s\\n\\n" "" | tr " " "-"
			"$NoARGs" && printf "${BOLD}INTERFACE: %-5s${CLEARFORMAT}\\n" "$IFACE"
			"$NoARGs" && printf "${BOLD}SSID: %-20s${CLEARFORMAT}\\n\\n" "$(nvram get "${IFACE}_ssid")"

			IFACE_MACS="$(wl -i "$IFACE" assoclist)"
			if [ "$IFACE_MACS" != "" ]; then
				"$NoARGs" && printf "${BOLD}%-30s%-20s%-20s%-15s%-15s%-10s%-5s${CLEARFORMAT}\\n" "HOSTNAME" "IP" "MAC" "CONNECTED" "RX/TX" "RSSI" "PHY"
				IFS=$'\n'
				for GUEST_MAC in $IFACE_MACS; do
					GUEST_MACADDR="$(echo "$GUEST_MAC" | awk '{print $2}')"

					##----------------------------------------##
					## Modified by Martinski W. [2023-Sep-11] ##
					##----------------------------------------##
					GUEST_ARPCOUNT="$(echo "$ARP_CACHE" | grep -ic "$GUEST_MACADDR")"
					if [ $GUEST_ARPCOUNT -lt 2 ]
					then
						GUEST_ARPENTRY="$(echo "$ARP_CACHE" | grep -i "$GUEST_MACADDR")"
						GUEST_ARPINFO="$(echo "$GUEST_ARPENTRY" | grep "$IFACE" | eval "$NOT_LANIP")"
					else
						GUEST_ARPENTRY="$(echo "$ARP_CACHE" | grep -i "$GUEST_MACADDR" | grep "$IFACE")"
						GUEST_ARPINFO="$(echo "$GUEST_ARPENTRY" | eval "$NOT_LANIP")"
					fi

					GUEST_IPADDR="$(echo "$GUEST_ARPINFO" | awk '{print $1}')"
					GUEST_ARPFLAG="$(echo "$GUEST_ARPENTRY" | awk '{print $3}')"

					# Skip guest client when ARP entry is marked as "incomplete" #
					if [ "$GUEST_ARPFLAG" = "0x0" ] ; then continue ; fi

					GUEST_HOST=""
					FOUND_IPADDR="$GUEST_IPADDR"

					if [ -z "$GUEST_IPADDR" ]
					then
						FOUND_IPADDR="$(grep -i "$GUEST_MACADDR" /var/lib/misc/dnsmasq.leases | awk '{print $3}')"
						GUEST_IPADDR="$(grep -i "$GUEST_MACADDR" /var/lib/misc/dnsmasq.leases | eval "$NOT_LANIP" | awk '{print $3}')"
					fi

					if [ -n "$GUEST_IPADDR" ]
					then
						GUEST_HOST="$(arp "$GUEST_IPADDR" | grep "$IFACE" | awk '{print $1}' | cut -f1 -d ".")"
						if [ -z "$GUEST_HOST" ] || [ "$GUEST_HOST" = "?" ]
						then
							GUEST_HOST=$(grep -i "$GUEST_MACADDR" /var/lib/misc/dnsmasq.leases | awk '{print $4}')
						fi

						if [ "$GUEST_HOST" = "?" ] || [ "$(printf "%s" "$GUEST_HOST" | wc -m)" -le 1 ]; then
							GUEST_HOST="$(nvram get custom_clientlist | grep -ioE "<.*>$GUEST_MACADDR" | awk -F ">" '{print $(NF-1)}' | tr -d '<')" #thanks Adamm00
						fi

						if [ -f /opt/bin/dig ]; then
							if [ -z "$GUEST_HOST" ]; then
								GUEST_HOST="$(/opt/bin/dig +short +answer -x "$GUEST_IPADDR" '@'"$(nvram get lan_ipaddr)" | cut -f1 -d'.')"
							fi
						fi
					elif [ -n "$FOUND_IPADDR" ]
					then
						GUEST_IPADDR="Not in Guest Net"
						GUEST_HOST="$(arp "$FOUND_IPADDR" | grep "$IFACE" | awk '{print $1}' | cut -f1 -d ".")"
						if [ -z "$GUEST_HOST" ] || [ "$GUEST_HOST" = "?" ]
						then
							GUEST_HOST=$(grep -i "$GUEST_MACADDR" /var/lib/misc/dnsmasq.leases | awk '{print $4}')
						fi
					else
						GUEST_IPADDR="Unknown"
					fi

					if [ -z "$GUEST_HOST" ] || [ "$GUEST_HOST" = "*" ]
					then GUEST_HOST="Unknown" ; fi

					GUEST_HOST=$(echo "$GUEST_HOST" | tr -d '\n')
					GUEST_IPADDR=$(echo "$GUEST_IPADDR" | tr -d '\n')
					GUEST_MACADDR=$(echo "$GUEST_MACADDR" | tr -d '\n')
					GUEST_RSSI=$(wl -i "$IFACE" rssi "$GUEST_MACADDR" | tr -d '\n')

					GUEST_STAINFO=$(wl -i "$IFACE" sta_info "$GUEST_MACADDR")
					GUEST_TIMECONNECTED=$(echo "$GUEST_STAINFO" | grep "in network" | awk '{print $3}' | tr -d '\n')
					GUEST_TIMECONNECTED_PRINT=$(printf '%dh:%dm:%ds\n' $((GUEST_TIMECONNECTED/3600)) $((GUEST_TIMECONNECTED%3600/60)) $((GUEST_TIMECONNECTED%60)))

					GUEST_TX=$(echo "$GUEST_STAINFO" | grep "rate of last tx pkt" | awk '{print $6}' | tr -d '\n' | awk '{printf("%.0f", $1/1000);}')
					GUEST_RX=$(echo "$GUEST_STAINFO" | grep "rate of last rx pkt" | awk '{print $6}' | tr -d '\n' | awk '{printf("%.0f", $1/1000);}')

					GUEST_PHY=""
					if echo "$GUEST_STAINFO" | grep -q "HT caps"; then
						GUEST_PHY="n"
					elif echo "$GUEST_STAINFO" | grep -q "VHT caps"; then
						GUEST_PHY="ac"
					else
						GUEST_PHY="Unknown"
					fi

					"$NoARGs" && printf "%-30s%-20s%-20s%-15s%-15s%-10s%-5s${CLEARFORMAT}\\n" "$GUEST_HOST" "$GUEST_IPADDR" "$GUEST_MACADDR" "$GUEST_TIMECONNECTED_PRINT" "$GUEST_RX/$GUEST_TX Mbps" "$GUEST_RSSI dBm" "$GUEST_PHY"

					printf "%s,%s,%s,%s,%s,%s,%s,%s,%s\\n" "$IFACE" "$GUEST_HOST" "$GUEST_IPADDR" "$GUEST_MACADDR" "$GUEST_TIMECONNECTED" "$GUEST_RX" "$GUEST_TX" "$GUEST_RSSI" "$GUEST_PHY" >> "$TMPSTATUSOUTPUTFILE"
				done
				unset IFS
			else
				"$NoARGs" && printf "${BOLD}${WARN}No clients connected${CLEARFORMAT}\\n\\n"
				printf "%s,,NOCLIENTS,,,,,,\\n" "$IFACE" >> "$TMPSTATUSOUTPUTFILE"
			fi
		fi
	done

	mv "$TMPSTATUSOUTPUTFILE" "$STATUSOUTPUTFILE" 2>/dev/null
	"$NoARGs" && printf "%75s\\n\\n" "" | tr " " "-"
	"$NoARGs" && printf "${BOLD}$PASS%sQuery complete, please see above for results${CLEARFORMAT}\\n\\n" ""
	#######################################################################################################
	renice 0 $$
}

Menu_Diagnostics(){
	printf "\\n${BOLD}This will collect the following. Files are encrypted with a unique random passphrase.${CLEARFORMAT}\\n"
	printf "\\n${BOLD} - iptables rules${CLEARFORMAT}"
	printf "\\n${BOLD} - ebtables rules${CLEARFORMAT}"
	printf "\\n${BOLD} - %s${CLEARFORMAT}" "$SCRIPT_CONF"
	printf "\\n${BOLD} - %s${CLEARFORMAT}" "$DNSCONF"
	printf "\\n${BOLD} - /jffs/scripts/firewall-start${CLEARFORMAT}"
	printf "\\n${BOLD} - /jffs/scripts/service-event${CLEARFORMAT}\\n\\n"
	while true; do
		printf "\\n${BOLD}Do you want to continue? (y/n)${CLEARFORMAT}  "
		read -r confirm
		case "$confirm" in
			y|Y)
				break
			;;
			n|N)
				printf "\\n${BOLD}User declined, returning to menu${CLEARFORMAT}\\n\\n"
				return 1
			;;
			*)
				printf "\\nPlease choose a valid option (y/n)\\n\\n"
			;;
		esac
	done

	printf "\\n\\n${BOLD}Generating %s diagnostics...${CLEARFORMAT}\\n\\n" "$SCRIPT_NAME"

	DIAGPATH="/tmp/${SCRIPT_NAME}Diag"
	mkdir -p "$DIAGPATH"

	iptables-save > "$DIAGPATH/iptables.txt"

	ebtables -L > "$DIAGPATH/ebtables.txt"
	echo "" >> "$DIAGPATH/ebtables.txt"
	ebtables -t broute -L >> "$DIAGPATH/ebtables.txt"

	ip rule show > "$DIAGPATH/iprule.txt"
	ip route show > "$DIAGPATH/iproute.txt"
	ip route show table all | grep "table" | sed 's/.*\(table.*\)/\1/g' | awk '{print $2}' | sort | uniq | grep ovpn > "$DIAGPATH/routetablelist.txt"

	while IFS='' read -r line || [ -n "$line" ]; do
		ip route show table "$line" > "$DIAGPATH/iproute_$line.txt"
	done < "$DIAGPATH/routetablelist.txt"

	ifconfig -a > "$DIAGPATH/ifconfig.txt"

	cp "$SCRIPT_CONF" "$DIAGPATH/$SCRIPT_NAME.conf"
	cp "$DNSCONF" "$DIAGPATH/$SCRIPT_NAME.dnsmasq"
	cp /jffs/scripts/dnsmasq.postconf "$DIAGPATH/dnsmasq.postconf"
	cp /jffs/scripts/firewall-start "$DIAGPATH/firewall-start"
	cp /jffs/scripts/service-event "$DIAGPATH/service-event"

	SEC="$(Generate_Random_String 32)"
	tar -czf "/tmp/$SCRIPT_NAME.tar.gz" -C "$DIAGPATH" .
	/usr/sbin/openssl enc -aes-256-cbc -k "$SEC" -e -in "/tmp/$SCRIPT_NAME.tar.gz" -out "/tmp/$SCRIPT_NAME.tar.gz.enc"

	Print_Output true "Diagnostics saved to /tmp/$SCRIPT_NAME.tar.gz.enc with passphrase $SEC" "$PASS"

	rm -f "/tmp/$SCRIPT_NAME.tar.gz" 2>/dev/null
	rm -rf "$DIAGPATH" 2>/dev/null
	SEC=""
}

Menu_Uninstall(){
	Print_Output true "Removing $SCRIPT_NAME..." "$PASS"
	Auto_Startup delete 2>/dev/null
	Auto_Cron delete 2>/dev/null
	Auto_DNSMASQ delete 2>/dev/null
	Auto_ServiceEvent delete 2>/dev/null
	Auto_ServiceStart delete 2>/dev/null
	Auto_OpenVPNEvent delete 2>/dev/null
	Avahi_Conf delete 2>/dev/null
	if [ "$(Firmware_Version_Check "$(nvram get buildno)")" -lt "$(Firmware_Version_Check 386.3)" ]; then
		Routing_NVRAM deleteall 2>/dev/null
	else
		Routing_VPNDirector deleteall 2>/dev/null
	fi
	Firewall_NAT deleteall 2>/dev/null
	Routing_RPDB delete 2>/dev/null
	Firewall_Chains deleteall 2>/dev/null
	Firewall_NVRAM deleteall "$IFACE" 2>/dev/null
	Iface_Manage deleteall 2>/dev/null
	DHCP_Conf deleteall 2>/dev/null
	LOCKFILE=/tmp/addonwebui.lock
	FD=386
	eval exec "$FD>$LOCKFILE"
	flock -x "$FD"
	Get_WebUI_Page "$SCRIPT_DIR/YazFi_www.asp"
	if [ -n "$MyPage" ] && [ "$MyPage" != "none" ] && [ -f "/tmp/menuTree.js" ]; then
		sed -i "\\~$MyPage~d" /tmp/menuTree.js
		umount /www/require/modules/menuTree.js
		mount -o bind /tmp/menuTree.js /www/require/modules/menuTree.js
		rm -f "$SCRIPT_WEBPAGE_DIR/$MyPage"
		rm -f "$SCRIPT_WEBPAGE_DIR/$(echo $MyPage | cut -f1 -d'.').title"
	fi
	flock -u "$FD"
	rm -f "$SCRIPT_DIR/YazFi_www.asp" 2>/dev/null
	while true; do
		printf "\\n${BOLD}Do you want to delete %s configuration file(s)? (y/n)${CLEARFORMAT}  " "$SCRIPT_NAME"
		read -r confirm
		case "$confirm" in
			y|Y)
				rm -rf "/jffs/addons/$SCRIPT_NAME.d" 2>/dev/null
				break
			;;
			*)
				break
			;;
		esac
	done
	SETTINGSFILE="/jffs/addons/custom_settings.txt"
	sed -i '/yazfi_version_local/d' "$SETTINGSFILE"
	sed -i '/yazfi_version_server/d' "$SETTINGSFILE"
	Shortcut_Script delete
	rm -f "/jffs/scripts/$SCRIPT_NAME" 2>/dev/null
	Clear_Lock
	Print_Output true "Restarting firewall to complete uninstall" "$PASS"
	service restart_dnsmasq >/dev/null 2>&1
	service restart_firewall >/dev/null 2>&1
}

Show_About(){
	cat <<EOF
About
  $SCRIPT_NAME is a Feature expansion of guest WiFi networks on
AsusWRT-Merlin, including SSID -> VPN, separate subnets per guest
network, pinhole access to LAN resources (e.g. DNS) and more!
License
  $SCRIPT_NAME is free to use under the GNU General Public License
  version 3 (GPL-3.0) https://opensource.org/licenses/GPL-3.0
Help & Support
  https://www.snbforums.com/forums/asuswrt-merlin-addons.60/?prefix_id=13
Source code
  https://github.com/jackyaz/$SCRIPT_NAME
EOF
	printf "\\n"
}
### ###

### function based on @dave14305's FlexQoS show_help function ###
Show_Help(){
	cat <<EOF
Available commands:
  $SCRIPT_NAME about              explains functionality
  $SCRIPT_NAME update             checks for updates
  $SCRIPT_NAME forceupdate        updates to latest version (force update)
  $SCRIPT_NAME startup            runs startup actions such as mount WebUI tab
  $SCRIPT_NAME install            installs script
  $SCRIPT_NAME uninstall          uninstalls script
  $SCRIPT_NAME runnow             apply $SCRIPT_NAME configuration
  $SCRIPT_NAME check              check if $SCRIPT_NAME configuration is still in effect and re-apply if not
  $SCRIPT_NAME bounceclients      restart guest network radios
  $SCRIPT_NAME status             print information about clients connected to $SCRIPT_NAME guest networks
  $SCRIPT_NAME check              check if $SCRIPT_NAME configuration is still in effect and re-apply if not
  $SCRIPT_NAME userscripts        run userscripts (if any have been created)
  $SCRIPT_NAME rejectlogging      toggle whether rejected packets are logged to syslog
  $SCRIPT_NAME develop            switch to development branch
  $SCRIPT_NAME stable             switch to stable branch
EOF
	printf "\\n"
}
### ###

if [ ! -f /opt/bin/qrencode ] && [ -f /opt/bin/opkg ]; then
	opkg update
	opkg install qrencode
fi

if [ $# -eq 0 ] || [ -z "$1" ]; then
	Create_Dirs
	Create_Symlinks
	Auto_Startup create 2>/dev/null
	Auto_Cron create 2>/dev/null
	Auto_DNSMASQ create 2>/dev/null
	Auto_ServiceEvent create 2>/dev/null
	Auto_ServiceStart create 2>/dev/null
	Auto_OpenVPNEvent create 2>/dev/null
	Shortcut_Script create
	ScriptHeader
	MainMenu
	exit 0
fi

case "$1" in
	install)
		Menu_Install
		exit 0
	;;
	runnow)
		Check_Lock
		Print_Output true "Firewall restarted - sleeping 10s before running $SCRIPT_NAME" "$PASS"
		sleep 10
		Config_Networks
		Clear_Lock
		exit 0
	;;
	check)
		if [ "$(grep -c "NextDNS" /jffs/scripts/dnsmasq.postconf)" -gt 0 ]; then
			if [ "$(grep -c "exit 0" /jffs/scripts/dnsmasq.postconf)" -gt 0 ]; then
				sed -i '/exit 0/d' /jffs/scripts/dnsmasq.postconf
				service restart_dnsmasq
			fi
		fi
		if ! iptables -nL | grep -q "YazFi"; then
			Check_Lock
			Print_Output true "$SCRIPT_NAME firewall rules not detected during persistence check, re-applying rules" "$WARN"
			Config_Networks
			Clear_Lock
			exit 0
		fi

		if ! Conf_Exists; then
			exit 1
		fi

		if ! Conf_Validate; then
			exit 1
		fi

		. $SCRIPT_CONF
		WIRELESSRESTART="false"
		for IFACE in $IFACELIST; do
			if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_ENABLED")" = "true" ]; then
				if [ "$(nvram get "${IFACE}_lanaccess")" != "on" ]; then
					nvram set "$IFACE"_lanaccess=on
					WIRELESSRESTART="true"
				fi
			fi
		done

		if [ "$WIRELESSRESTART" = "true" ]; then
			nvram commit
			service restart_wireless >/dev/null 2>&1
		fi

		exit 0
	;;
	startup)
		sleep 12
		Create_Dirs
		Create_Symlinks
		Auto_Cron create 2>/dev/null
		Mount_WebUI
		exit 0
	;;
	bounceclients)
		. "$SCRIPT_CONF"
		Iface_BounceClients
		exit 0;
	;;
	service_event)
		if [ "$2" = "restart" ] && [ "$3" = "wireless" ]; then
			Check_Lock
			Print_Output true "Wireless restarted - sleeping 30s before running $SCRIPT_NAME" "$PASS"
			sleep 30
			Config_Networks
			Clear_Lock
		elif [ "$2" = "start" ] && [ "$3" = "$SCRIPT_NAME" ]; then
			Conf_FromSettings
			Print_Output true "WebUI config updated - running $SCRIPT_NAME" "$PASS"
			Check_Lock
			Config_Networks
			Clear_Lock
		elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}checkupdate" ]; then
			Update_Check
			exit 0
		elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}doupdate" ]; then
			Update_Version force unattended
			exit 0
		elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}connectedclients" ]; then
			STATUSOUTPUTFILE="$SCRIPT_DIR/.connectedclients"
			rm -f "$STATUSOUTPUTFILE"
			sleep 2
			Menu_Status outputtofile
			exit 0
		fi
		exit 0
	;;
	openvpn)
		if echo "$2" | grep -q tun1 && [ "$3" = "route-up" ]; then
			Print_Output true "VPN tunnel route just came up, running YazFi to fix RPDB routing" "$PASS"
			sleep 5

			if ! Conf_Exists; then
				return 1
			fi

			if ! Conf_Validate; then
				return 1
			fi

			. $SCRIPT_CONF

			for IFACE in $IFACELIST; do
				if [ "$(eval echo '$'"$(Get_Iface_Var "$IFACE")_ENABLED")" = "true" ]; then
					Routing_RPDB create "$IFACE" 2>/dev/null
				fi
			done
		fi
	;;
	status)
		Menu_Status
		exit 0
	;;
	statustofile)
		Menu_Status outputtofile
		exit 0
	;;
	userscripts)
		Execute_UserScripts
		exit 0
	;;
	rejectlogging)
		if [ -f "$SCRIPT_DIR/.rejectlogging" ]; then
			rm -f "$SCRIPT_DIR/.rejectlogging"
		else
			touch "$SCRIPT_DIR/.rejectlogging"
		fi
		service restart_firewall >/dev/null 2>&1
		exit 0
	;;
	update)
		Update_Version unattended
		exit 0
	;;
	forceupdate)
		Update_Version force unattended
		exit 0
	;;
	setversion)
		Set_Version_Custom_Settings local "$SCRIPT_VERSION"
		Set_Version_Custom_Settings server "$SCRIPT_VERSION"
		Create_Dirs
		Create_Symlinks
		Auto_Startup create 2>/dev/null
		Auto_Cron create 2>/dev/null
		Auto_DNSMASQ create 2>/dev/null
		Auto_ServiceEvent create 2>/dev/null
		Auto_ServiceStart create 2>/dev/null
		Auto_OpenVPNEvent create 2>/dev/null
		Shortcut_Script create
		exit 0
	;;
	postupdate)
		Create_Dirs
		Create_Symlinks
		Auto_Startup create 2>/dev/null
		Auto_Cron create 2>/dev/null
		Auto_DNSMASQ create 2>/dev/null
		Auto_ServiceEvent create 2>/dev/null
		Auto_ServiceStart create 2>/dev/null
		Auto_OpenVPNEvent create 2>/dev/null
		Shortcut_Script create
		exit 0
	;;
	uninstall)
		Menu_Uninstall
		exit 0
	;;
	develop)
		SCRIPT_BRANCH="develop"
		SCRIPT_REPO="https://jackyaz.io/$SCRIPT_NAME/$SCRIPT_BRANCH"
		Update_Version force
		exit 0
	;;
	stable)
		SCRIPT_BRANCH="master"
		SCRIPT_REPO="https://jackyaz.io/$SCRIPT_NAME/$SCRIPT_BRANCH"
		Update_Version force
		exit 0
	;;
	about)
		ScriptHeader
		Show_About
		exit 0
	;;
	help)
		ScriptHeader
		Show_Help
		exit 0
	;;
	*)
		ScriptHeader
		Print_Output false "Command not recognised." "$ERR"
		Print_Output false "For a list of available commands run: $SCRIPT_NAME help"
		exit 1
	;;
esac
