From 9319a26ac3f33d13ebe704163a1a92c8f8c12ca5 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Wed, 6 Aug 2025 22:12:58 +0000 Subject: [PATCH 01/48] Update enhancements.txt --- enhancements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/enhancements.txt b/enhancements.txt index b3ad86e..7dae014 100644 --- a/enhancements.txt +++ b/enhancements.txt @@ -1,5 +1,6 @@ To-Do: - If the WAN doesn't respond after X minutes, issue a router reboot? +- Remove RL tag if amtm email functionality is turned off completely Done: - Option to show/hide OVPN and WG sections. @Stephen Harrington From f90291e228dc996e2da1b3a397d2eedd75884d21 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Wed, 6 Aug 2025 22:14:02 +0000 Subject: [PATCH 02/48] Update enhancements.txt --- enhancements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/enhancements.txt b/enhancements.txt index 7dae014..97762e8 100644 --- a/enhancements.txt +++ b/enhancements.txt @@ -1,6 +1,7 @@ To-Do: - If the WAN doesn't respond after X minutes, issue a router reboot? - Remove RL tag if amtm email functionality is turned off completely +- Specify how many times you want cycle to go before a reset occurs (right now it's every cycle, but could make it 2 or 3?) Done: - Option to show/hide OVPN and WG sections. @Stephen Harrington From 8a15fc743b31289cd8135a6eb39e874f3ba43665 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Fri, 8 Aug 2025 06:06:13 -0400 Subject: [PATCH 03/48] Update enhancements.txt --- enhancements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/enhancements.txt b/enhancements.txt index 97762e8..cc262e3 100644 --- a/enhancements.txt +++ b/enhancements.txt @@ -2,6 +2,7 @@ To-Do: - If the WAN doesn't respond after X minutes, issue a router reboot? - Remove RL tag if amtm email functionality is turned off completely - Specify how many times you want cycle to go before a reset occurs (right now it's every cycle, but could make it 2 or 3?) +- Get unbound over WG working Done: - Option to show/hide OVPN and WG sections. @Stephen Harrington From 89e8771a0934087c8c29c0c657e6f359cabeb9e3 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Sat, 9 Aug 2025 01:46:58 +0000 Subject: [PATCH 04/48] Update enhancements.txt --- enhancements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/enhancements.txt b/enhancements.txt index cc262e3..11e7371 100644 --- a/enhancements.txt +++ b/enhancements.txt @@ -1,6 +1,6 @@ To-Do: - If the WAN doesn't respond after X minutes, issue a router reboot? -- Remove RL tag if amtm email functionality is turned off completely +- Remove RL tag if amtm email functionality is turned off completely, and show some info in the config menu on the AMTM email item. - Specify how many times you want cycle to go before a reset occurs (right now it's every cycle, but could make it 2 or 3?) - Get unbound over WG working From e15773d681db7b5f3bf973c460c5eb8c2ffe8714 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Sat, 9 Aug 2025 01:53:58 +0000 Subject: [PATCH 05/48] Update enhancements.txt --- enhancements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/enhancements.txt b/enhancements.txt index 11e7371..9637621 100644 --- a/enhancements.txt +++ b/enhancements.txt @@ -1,10 +1,11 @@ To-Do: - If the WAN doesn't respond after X minutes, issue a router reboot? -- Remove RL tag if amtm email functionality is turned off completely, and show some info in the config menu on the AMTM email item. - Specify how many times you want cycle to go before a reset occurs (right now it's every cycle, but could make it 2 or 3?) - Get unbound over WG working Done: +- Remove RL tag if amtm email functionality is turned off completely, and show some info in the config menu on the AMTM email item. + - Option to show/hide OVPN and WG sections. @Stephen Harrington - Wireguard compatibility - *** if downloaded list = 0, then dont save list and preserve, and email From 9c28c0a13576900fd62092ce602c8eb53b421866 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Sat, 9 Aug 2025 15:23:25 +0000 Subject: [PATCH 06/48] Update changelog.txt --- changelog.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/changelog.txt b/changelog.txt index 70220b8..b8def4b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,7 @@ +v1.5.1 - (TBA) + - PATCH: Based on a recommendation from @JGrana, the RL (Email Rate Limiting) indicators will no longer + show up on the main UI (or in the config display) when AMTM email functionality is turned off. + v1.5.0 - (August 3, 2025) - MINOR: Well, actually kinda major. Wireguard is now formally being supported! VPNMON-R3 will show all 5 Wireguard slots for more modern routers. Those who can only physically support 2 VPN slots will From 61daf1deccc17348cec278a70aa376d37a0f1017 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:58:02 +0000 Subject: [PATCH 07/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 502 +++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 449 insertions(+), 53 deletions(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index 5c109ba..8be6903 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -1,6 +1,6 @@ #!/bin/sh -# VPNMON-R3 v1.5.0 (VPNMON-R3.SH) is an all-in-one script that is optimized to maintain multiple VPN connections and is +# VPNMON-R3 v1.6.0b1 (VPNMON-R3.SH) is an all-in-one script that is optimized to maintain multiple VPN connections and is # able to provide for the capabilities to randomly reconnect using a specified server list containing the servers of your # choice. Special care has been taken to ensure that only the VPN connections you want to have monitored are tended to. # This script will check the health of up to 5 VPN connections on a regular interval to see if monitored VPN conenctions @@ -14,8 +14,8 @@ export PATH="/sbin:/bin:/usr/sbin:/usr/bin:$PATH" #Static Variables - please do not change -version="1.5.0" # Version tracker -beta=0 # Beta switch +version="1.6.0b1" # Version tracker +beta=1 # Beta switch screenshotmode=0 # Switch to present bogus info for screenshots apppath="/jffs/scripts/vpnmon-r3.sh" # Static path to the app logfile="/jffs/addons/vpnmon-r3.d/vpnmon-r3.log" # Static path to the log @@ -31,7 +31,8 @@ schedulehrs=1 # Scheduler hour schedulemin=0 # Scheduler mins autostart=0 # Auto start on router reboot y/n unboundclient=0 # Unbound bound to VPN client slot# -unboundshowip=0 # Show expanded Unbound IP on UI +unboundwgclient=0 # Unbound bound to WG client slot# +unboundshowip=0 # Show expanded Unbound VPN IP on UI ResolverTimer=1 # Timer to give DNS resolver time to settle vpnping=0 # Tracking VPN Tunnel Pings refreshserverlists=0 # Tracking Automated Custom VPN Server List Reset @@ -867,6 +868,17 @@ do unboundclientexp="Enabled, VPN$unboundclient" fi + if [ "$unboundwgclient" -eq 0 ]; then + unboundwgclientexp="Disabled" + else + unboundwgclientexp="Enabled, WGC$unboundwgclient" + fi + + if [ "$unboundclient" -eq 0 ] && [ "$unboundwgclient" -eq 0 ]; then + unboundshowip=0 + saveconfig + fi + if [ "$unboundshowip" -eq 0 ]; then unboundshowipdisp="Disabled" else @@ -916,6 +928,16 @@ do amtmemailsuccfaildisp="Disabled" fi + rldisp="" + if [ "$amtmemailsuccess" = "1" ] || [ "$amtmemailfailure" = "1" ] + then + if [ "$ratelimit" = "0" ]; then + rldisp="| ${CRed}RL" + else + rldisp="| ${CGreen}RL:$ratelimit/h" + fi + fi + if [ "$rstspdmerlin" -eq 0 ]; then rstspdmerlindisp="Disabled" else @@ -933,23 +955,24 @@ do echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 2)${CClear} : Custom PING host to determine VPN health : ${CGreen}$PINGHOST" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 3)${CClear} : Custom Event Log size (rows) : ${CGreen}$logsize" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 4)${CClear} : Unbound DNS Lookups over VPN Integration : ${CGreen}$unboundclientexp" - if [ "$unboundclient" -eq 0 ]; then - echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 5)${CClear} -- ${CDkGray}Show Expanded Unbound IP Info? : $unboundshowipdisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 5)${CClear} : Unbound DNS Lookups over WG Integration : ${CGreen}$unboundwgclientexp" + if [ "$unboundclient" -ge 1 ] || [ "$unboundwgclient" -ge 1 ]; then + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 6)${CClear} -- Show Expanded Unbound IP Info? : ${CGreen}$unboundshowipdisp" else - echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 5)${CClear} -- Show Expanded Unbound IP Info? : ${CGreen}$unboundshowipdisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 6)${CClear} -- ${CDkGray}Show Expanded Unbound IP Info? : $unboundshowipdisp" fi - echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 6)${CClear} : Refresh Custom Server Lists on -RESET Switch : ${CGreen}$refreshserverlistsdisp" - echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 7)${CClear} : Provide additional WAN/Dual WAN monitoring : ${CGreen}$monitorwandisp" - echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 8)${CClear} : Enable/Disable VPN/WG Slot Monitoring : $useovpnwgDisp" - echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 9)${CClear} : Whitelist VPN Server IP Lists in Skynet : ${CGreen}$updateskynetdisp" - echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(10)${CClear} : AMTM Email Notifications on Success/Failure : ${CGreen}$amtmemailsuccfaildisp" - echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(11)${CClear} : Reset spdMerlin Interfaces on VPN Reset : ${CGreen}$rstspdmerlindisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 7)${CClear} : Refresh Custom Server Lists on -RESET Switch : ${CGreen}$refreshserverlistsdisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 8)${CClear} : Provide additional WAN/Dual WAN monitoring : ${CGreen}$monitorwandisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 9)${CClear} : Enable/Disable VPN/WG Slot Monitoring : $useovpnwgDisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(10)${CClear} : Whitelist VPN Server IP Lists in Skynet : ${CGreen}$updateskynetdisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(11)${CClear} : AMTM Email Notifications / Rate Limiting : ${CGreen}$amtmemailsuccfaildisp $rldisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(12)${CClear} : Reset spdMerlin Interfaces on VPN Reset : ${CGreen}$rstspdmerlindisp" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} | ${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( e)${CClear} : Exit${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo "" - read -p "Please select? (1-11, e=Exit): " SelectSlot + read -p "Please select? (1-12, e=Exit): " SelectSlot case $SelectSlot in 1) clear @@ -1257,7 +1280,274 @@ do ;; 5) - if [ "$unboundclient" != "0" ]; then + while true; do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Unbound DNS Lookups over WG Integration ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate if you would like to enable an integration with Unbound which allows${CClear}" + echo -e "${InvGreen} ${CClear} you to force any DNS lookups that Unbound would normally handle, but over your WG${CClear}" + echo -e "${InvGreen} ${CClear} connection, essentially allowing you to appear as your own DNS resolver. DNS resolver${CClear}" + echo -e "${InvGreen} ${CClear} traffic generated by Unbound is unencrypted as it queries DNS Root Servers by default.${CClear}" + echo -e "${InvGreen} ${CClear} This integration encrypts your DNS Resolver traffic up to your Public WG IP address,${CClear}" + echo -e "${InvGreen} ${CClear} after which it goes unencrypted to the DNS Root Servers, preventing any ISP or other${CClear}" + echo -e "${InvGreen} ${CClear} traffic inspection snooping when sending this across your WAN connection.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} IMPORTANT: This integration will only work with 1 primary WG connection that is${CClear}" + echo -e "${InvGreen} ${CClear} designated as the connection that will carry DNS resolution traffic. This integration${CClear}" + echo -e "${InvGreen} ${CClear} will work when multiple WG connections are running, but the other WG connections may${CClear}" + echo -e "${InvGreen} ${CClear} not be able to utilize the Unbound functionality, and considered standalone. You will${CClear}" + echo -e "${InvGreen} ${CClear} need to identify one WG Client Slot that will remain on at all times. This integration${CClear}" + echo -e "${InvGreen} ${CClear} is much more elegant and simple when it compares to the Unbound over VPN integration.${CClear}" + echo -e "${InvGreen} ${CClear} Should your WG connection go down, browsing/DNS resolution will continue to function${CClear}" + echo -e "${InvGreen} ${CClear} by traversing directly out over the WAN.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${CRed}WARNING: As of around March 1 2024, the Unbound integration no longer works with the${CClear}" + echo -e "${InvGreen} ${CClear} ${CRed}NordVPN Service due to possible blocking on their end. This service continues to work${CClear}" + echo -e "${InvGreen} ${CClear} ${CRed}as advertised with other VPN Providers, like AirVPN. Use at your own risk.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${CGreen}Requirements:" + echo -e "${InvGreen} ${CClear} ${CGreen}1. Unbound must already be installed and functioning (AMTM)" + echo -e "${InvGreen} ${CClear} ${CGreen}2. One static WG Slot must be designated for Unbound-over-WG use" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${CGreen}The following will be added/modified:" + echo -e "${InvGreen} ${CClear} ${CGreen}1. A new virtual br0 interface will be created on 10.99.88.77 (added)" + echo -e "${InvGreen} ${CClear} ${CGreen}2. A new ip rule will be added to point your WG Slot to this new br0 interface (added)" + echo -e "${InvGreen} ${CClear} ${CGreen}3. The unbound.conf will have its outgoing-interface modified to 10.99.88.77 (modified)" + echo -e "${InvGreen} ${CClear} ${CGreen}4. Unbound service will be restarted.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} (Default = 0 / Disabled)${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo "" + if [ $unboundwgclient -eq 0 ]; then + unboundwgclientexp="Disabled" + else + unboundwgclientexp="Enabled, WGC$unboundwgclient" + fi + + echo -e "${CClear}Current: ${CGreen}$unboundwgclientexp${CClear}" + echo "" + read -p "Please enter value (Disabled=0, WG Client=1-5)? (e=Exit): " unboundoverwg + + if [ ! -d "/jffs/addons/unbound" ]; then + echo "" + echo -e "${CRed}[Unbound was not detected on this system. Exiting...]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: Unbound was not detected on this system." >> $logfile + unboundwgclient=0 + saveconfig + sleep 3 + break + fi + + if [ "$unboundoverwg" = "0" ] || [ "$unboundoverwg" = "1" ] || [ "$unboundoverwg" = "2" ] || [ "$unboundoverwg" = "3" ] || [ "$unboundoverwg" = "4" ] || [ "$unboundoverwg" = "5" ] || [ "$unboundoverwg" = "e" ]; then + if [ "$unboundoverwg" = "0" ]; then + + # Delete all additions made to files to enable Unbound over VPN functionality + echo "" + echo -e "${CGreen}[Disabling Unbound over WG]...${CClear}" + sleep 2 + + # =============================================== + # === Disable Unbound-over-WG Integration === + # === Methodology engineered by @ZebMcKayhan === + # =============================================== + + # Modify unbound.conf file - set major variables + UNBOUND_CONF="/opt/var/lib/unbound/unbound.conf" + DISABLED_LINE="#outgoing-interface: 10.0.20.1 # v1.08 Martineau Use VPN tunnel to hide Root server queries from ISP (or force WAN ONLY)" + + # Check if the unbound.conf file exists and is readable. + if [ ! -r "${UNBOUND_CONF}" ]; then + echo "" + echo -e "${CRed}Error: Unbound Configuration file not found or not readable at ${UNBOUND_CONF}${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Unbound Configuration file not found or not readable at ${UNBOUND_CONF}" >> $logfile + sleep 3 + break + fi + + # Create a temp file to move unbound.conf contents into + TMP_FILE="${UNBOUND_CONF}.$$" + + # Remove outgoing-interface contents and replace with a dummy line + sed "s/^[[:space:]]*#*[[:space:]]*outgoing-interface:.*/${DISABLED_LINE}/g" "${UNBOUND_CONF}" > "${TMP_FILE}" + echo "" + echo -e "${CGreen}[Disabled 'outgoing-interface' and set back to default]...${CClear}" + sleep 1 + + # Ensure the temporary file was created and is not empty before overwriting the original + if [ ! -s "${TMP_FILE}" ]; then + echo -e "${CRed}Error: Failed to modify configuration. The temporary file is empty or was not created.${CClear}" + echo -e "${CRed}No changes have been made to ${UNBOUND_CONF}.${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Failed to modify configuration. The temporary file is empty or was not created." >> $logfile + sleep 3 + break + fi + + # Move the temp file into the unbound.conf file + mv "${TMP_FILE}" "${UNBOUND_CONF}" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Modified unbound.conf file back to defaults]...${CClear}" + sleep 1 + + # Restart unbound service + if [ -f "/jffs/addons/unbound/unbound_manager.sh" ]; then + unbound_manager restart >/dev/null 2>&1 + RESTART_STATUS=$? + if [ ${RESTART_STATUS} -eq 0 ]; then + echo "" + echo -e "${CGreen}[Unbound service restarted successfully]...${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Unbound service restarted successfully" >> $logfile + sleep 1 + else + echo "" + echo -e "${CRed}[Warning: 'unbound_manager restart' command failed with status ${RESTART_STATUS}]...${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: 'unbound_manager restart' command failed with status ${RESTART_STATUS}" >> $logfile + sleep 3 + fi + else + echo "" + echo -e "${CRed}[Warning: 'unbound_manager.sh' script not found. Could not restart service]...${CClear}" + echo -e "${CRed}[Please restart the unbound service manually]...${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: 'unbound_manager.sh' script not found. Could not restart service" >> $logfile + sleep 3 + fi + + # Remove the IP rule from VPN Director + ip rule del prio 11 >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Removed IP rule for virtual br0 interface at 10.99.88.77]...${CClear}" + sleep 1 + + # Remove added virtual br0 interface + ifconfig br0:UnboundWG down >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Removed virtual br0 interface at 10.99.88.77]...${CClear}" + sleep 1 + + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Unbound-over-WG was removed from VPNMON-R3" >> $logfile + unboundwgclient=0 + saveconfig + sleep 3 + break + + elif [ "$unboundoverwg" = "1" ] || [ "$unboundoverwg" = "2" ] || [ "$unboundoverwg" = "3" ] || [ "$unboundoverwg" = "4" ] || [ "$unboundoverwg" = "5" ]; then + + if [ "$unboundoverwg" = "$unboundwgclient" ]; then + echo -e "${CClear}\n[Unbound over WGC$unboundoverwg Already Active]"; sleep 3; break + fi + + if [ "$unboundoverwg" = "1" ] || [ "$unboundoverwg" = "2" ] || [ "$unboundoverwg" = "3" ] || [ "$unboundoverwg" = "4" ] || [ "$unboundoverwg" = "5" ] && [ $unboundwgclient -ge 1 ]; then + echo "" + echo -e "${CRed}When changing a WG Client Slot (from WGC #$unboundwgclient to WGC #$unboundoverwg), please proceed to 'Disable'" + echo -e "first (option 0), then choose a new WG Slot.${CClear}" + echo "" + sleep 3 + read -rsp $'Press any key to continue...\n' -n1 key + break + fi + + # =============================================== + # === Create Unbound-over-WG Integration === + # === Methodology engineered by @ZebMcKayhan === + # =============================================== + + echo "" + echo -e "${CGreen}[Enabling Unbound over WG]...${CClear}" + sleep 2 + + # Create new virtual br0 interface + ifconfig br0:UnboundWG 10.99.88.77 netmask 255.255.255.255 up >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Added virtual br0 interface at 10.99.88.77]...${CClear}" + sleep 1 + + # Point WG slot to new br0 interface for VPN Director + ip rule add from 10.99.88.77 lookup wgc$unboundoverwg prio 11 >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Pointed wgc$unboundoverwg to virtual br0 interface at 10.99.88.77]...${CClear}" + sleep 1 + + # Modify unbound.conf file - set major variables + UNBOUND_CONF="/opt/var/lib/unbound/unbound.conf" + ENABLED_IP="10.99.88.77" + COMMENT="# v1.08 Martineau Use VPN tunnel to hide Root server queries from ISP (or force WAN ONLY)" + + # Check if the unbound.conf file exists and is readable. + if [ ! -r "${UNBOUND_CONF}" ]; then + echo "" + echo -e "${CRed}Error: Unbound Configuration file not found or not readable at ${UNBOUND_CONF}${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Unbound Configuration file not found or not readable at ${UNBOUND_CONF}" >> $logfile + sleep 3 + break + fi + + # Create a temp file to move unbound.conf contents into + TMP_FILE="${UNBOUND_CONF}.$$" + + # Create new outgoing-interface contents + ENABLED_LINE="outgoing-interface: ${ENABLED_IP} ${COMMENT}" + sed "s/^[[:space:]]*#*[[:space:]]*outgoing-interface:.*/${ENABLED_LINE}/g" "${UNBOUND_CONF}" > "${TMP_FILE}" + + # Ensure the temporary file was created and is not empty before overwriting the original + if [ ! -s "${TMP_FILE}" ]; then + echo -e "${CRed}Error: Failed to modify configuration. The temporary file is empty or was not created.${CClear}" + echo -e "${CRed}No changes have been made to ${UNBOUND_CONF}.${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Failed to modify configuration. The temporary file is empty or was not created." >> $logfile + sleep 3 + break + fi + + # Move the temp file into the unbound.conf file + mv "${TMP_FILE}" "${UNBOUND_CONF}" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Modified unbound.conf file 'outgoing-interface: 10.99.88.77']...${CClear}" + sleep 1 + + # Restart unbound service + if [ -f "/jffs/addons/unbound/unbound_manager.sh" ]; then + unbound_manager restart >/dev/null 2>&1 + RESTART_STATUS=$? + if [ ${RESTART_STATUS} -eq 0 ]; then + echo "" + echo -e "${CGreen}[Unbound service restarted successfully]...${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Unbound service restarted successfully" >> $logfile + sleep 1 + else + echo "" + echo -e "${CRed}[Warning: 'unbound_manager restart' command failed with status ${RESTART_STATUS}]...${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: 'unbound_manager restart' command failed with status ${RESTART_STATUS}" >> $logfile + sleep 3 + fi + else + echo "" + echo -e "${CRed}[Warning: 'unbound_manager.sh' script not found. Could not restart service]...${CClear}" + echo -e "${CRed}[Please restart the unbound service manually]...${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: 'unbound_manager.sh' script not found. Could not restart service" >> $logfile + sleep 3 + fi + + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Unbound-over-WG was enabled for VPNMON-R3" >> $logfile + echo -e "${CClear}" + unboundwgclient=$unboundoverwg + saveconfig + echo "" + echo "Please consider rebooting your router now if this is your first time or have re-enabled Unbound-over-WG" + echo "" + sleep 3 + read -rsp $'Press any key to continue...\n' -n1 key + break + + elif [ "$unboundoverwg" = "e" ]; then + echo -e "${CClear}\n[Exiting]"; sleep 2; break + fi + + else + echo -e "${CClear}\n[Exiting]"; sleep 2; break + fi + done + ;; + + 6) + if [ "$unboundclient" != "0" ] || [ "$unboundwgclient" != "0" ]; then clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Show Expanded Unbound IP Information ${CClear}" echo -e "${InvGreen} ${CClear}" @@ -1296,7 +1586,7 @@ do fi ;; - 6) + 7) clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Refresh Custom Server Lists on -RESET Switch ${CClear}" echo -e "${InvGreen} ${CClear}" @@ -1335,7 +1625,7 @@ do fi ;; - 7) + 8) clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Provide WAN/Dual WAN Monitoring Functionality ${CClear}" echo -e "${InvGreen} ${CClear}" @@ -1375,7 +1665,7 @@ do fi ;; - 8) + 9) while true do clear @@ -1426,7 +1716,7 @@ do ;; - 9) + 10) clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Whitelist VPN Server IP Lists in Skynet ${CClear}" echo -e "${InvGreen} ${CClear}" @@ -1469,12 +1759,12 @@ do [Ee]) echo -e "${CClear}\n[Exiting]"; sleep 2; break ;; - 10) + 11) amtmevents source "$config" ;; - 11) + 12) clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Reset spdMerlin Interfaces on VPN Reset ${CClear}" echo -e "${InvGreen} ${CClear}" @@ -1785,8 +2075,8 @@ do echo -e "${InvGreen} ${CClear} Use the corresponding ${CGreen}()${CClear} key to enable/disable email event notifications:${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" - if [ "$amtmemailsuccess" = "1" ]; then amtmemailsuccessdisp="${CGreen}Y${CCyan}"; else amtmemailsuccess=0; amtmemailsuccessdisp="${CRed}N${CCyan}"; fi - if [ "$amtmemailfailure" = "1" ]; then amtmemailfailuredisp="${CGreen}Y${CCyan}"; else amtmemailfailure=0; amtmemailfailuredisp="${CRed}N${CCyan}"; fi + if [ "$amtmemailsuccess" = "1" ]; then amtmemailsuccessdisp="${CGreen}Y${CCyan}"; else amtmemailsuccess=0; amtmemailsuccessdisp="${CRed}N${CCyan}"; saveconfig; fi + if [ "$amtmemailfailure" = "1" ]; then amtmemailfailuredisp="${CGreen}Y${CCyan}"; else amtmemailfailure=0; amtmemailfailuredisp="${CRed}N${CCyan}"; saveconfig; fi if [ "$ratelimit" = "0" ]; then ratelimitdisp="Disabled"; else ratelimitdisp=$ratelimit; fi echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN Success Event Notifications${CClear} ${CGreen}(1) -${CClear} $amtmemailsuccessdisp${CClear}" @@ -3559,6 +3849,7 @@ saveconfig() echo 'wgautomation5="'"$wgautomation5"'"' echo 'refreshserverlists='$refreshserverlists echo 'unboundclient='$unboundclient + echo 'unboundwgclient='$unboundwgclient echo 'unboundshowip='$unboundshowip echo 'monitorwan='$monitorwan echo 'useovpn='$useovpn @@ -3571,6 +3862,11 @@ saveconfig() } > "$config" echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New vpnmon-r3.cfg File Saved" >> $logfile + + if [ -f "$config" ] + then + source "$config" + fi } # ------------------------------------------------------------------------------------------------------------------------- @@ -3817,6 +4113,18 @@ sendmessage() printf "Please check your network environment and configuration if this error continues to persist." printf "\n" } > "$tmpEMailBodyFile" + elif [ "$2" = "WG Slot Not Synced With Unbound" ]; then + emailSubject="WARNING: WG Slot $3 Not Synced with Unbound" + emailBodyTitle="WARNING: WG Slot $3 Not Synced with Unbound" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "WARNING: VPNMON-R3 has detected that WG Slot $3 is not synced with Unbound. WG Slot $3 has been reset.\n" + printf "Please check your network environment and configuration if this error continues to persist." + printf "\n" + } > "$tmpEMailBodyFile" ##-------------------------------------## ## Modified by Dan G. [2025-Jul-16] ## ##-------------------------------------## @@ -4655,9 +4963,6 @@ getwgip() icanhazwgip="$(eval $icanhazwgip)" if [ -z "$icanhazwgip" ] || echo "$icanhazwgip" | grep -qoE 'Internet|traffic|Error|error' ; then icanhazwgip="0.0.0.0" ; fi - # Added based on suggestion from @ZebMcKayhan - ip rule del prio 10 >/dev/null 2>&1 - if [ -z "$icanhazwgip" ] then wgip="000.000.000.000" @@ -4666,6 +4971,61 @@ getwgip() wgip="$(printf '%15s' "$icanhazwgip")" fi + if [ "$unboundwgclient" -ne 0 ] && [ "$unboundwgclient" -eq "$1" ] + then + if [ "$ResolverTimer" -eq 1 ]; then + ResolverTimer=0 + if [ "$unboundshowip" -eq 0 ]; then + ubsync="${CYellow}-?[UB]${CClear}" + else + ubsync="${CYellow}-?[UB:Resolving]${CClear}" + fi + else + # Huge thanks to @SomewhereOverTheRainbow for his expertise in troublshooting and coming up with this DNS Resolver methodology! + DNSResolver="$({ unbound-control flush whoami.akamai.net >/dev/null 2>&1; } && dig whoami.akamai.net +short @"$(netstat -nlp 2>/dev/null | awk '/.*(unbound){1}.*/{split($4, ip_addr, ":");if(substr($4,11) !~ /.*953.*/)print ip_addr[1];if(substr($4,11) !~ /.*953.*/)exit}')" -p "$(netstat -nlp 2>/dev/null | awk '/.*(unbound){1}.*/{if(substr($4,11) !~ /.*953.*/)print substr($4,11);if(substr($4,11) !~ /.*953.*/)exit}')" 2>/dev/null)" + + if [ -z "$DNSResolver" ] + then + if [ "$unboundshowip" -eq 0 ]; then + ubsync="${CRed}-X[UB]${CClear}" + else + ubsync="${CRed}-X[UB:$DNSResolver]${CClear}" + fi + # rudimentary check to make sure value coming back is in the format of an IP address... Don't care if it's more than 255. + elif expr "$DNSResolver" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null + then + # If the DNS resolver and public VPN IP address don't match in our Unbound scenario, reset! + if [ "$DNSResolver" != "$icanhazwgip" ] + then + if [ "$unboundshowip" -eq 0 ]; then + ubsync="${CRed}-X[UB]${CClear}" + else + ubsync="${CRed}-X[UB:$DNSResolver]${CClear}" + fi + ResolverTimer=1 + unboundreset=$1 + else + if [ "$unboundshowip" -eq 0 ]; then + ubsync="${CGreen}->[UB]${CClear}" + else + ubsync="${CGreen}->[UB:$DNSResolver]${CClear}" + fi + fi + else + if [ "$unboundshowip" -eq 0 ]; then + ubsync="${CYellow}-?[UB]${CClear}" + else + ubsync="${CYellow}-?[UB:$DNSResolver]${CClear}" + fi + fi + fi + else + ubsync="" + fi + + # Added based on suggestion from @ZebMcKayhan + ip rule del prio 10 >/dev/null 2>&1 + # Insert bogus IP if screenshotmode is on # if [ "$screenshotmode" = "1" ]; then wgip="$(printf '%15s' "12.34.56.78")" @@ -5383,17 +5743,21 @@ displayopsmenu() amtmdisp="${CDkGray}Disabled " fi - if [ "$ratelimit" = "0" ]; then - rldisp="${CRed}RL" - else - rldisp="${CGreen}RL:$ratelimit/h" + rldisp="" + if [ "$amtmemailsuccess" = "1" ] || [ "$amtmemailfailure" = "1" ] + then + if [ "$ratelimit" = "0" ]; then + rldisp="| ${CRed}RL" + else + rldisp="| ${CGreen}RL:$ratelimit/h" + fi fi #display operations menu if [ "$availableslots" = "1 2" ] then echo -e "${InvGreen} ${InvDkGray}${CWhite} Operations Menu ${CClear}" - echo -e "${InvGreen} ${CClear} Reset/Reconnect VPN 1:${CGreen}(1)${CClear} 2:${CGreen}(2)${CClear} ${InvGreen} ${CClear} ${CGreen}(C)${CClear}onfiguration Menu / Main Setup Menu | $rldisp${CClear}" + echo -e "${InvGreen} ${CClear} Reset/Reconnect VPN 1:${CGreen}(1)${CClear} 2:${CGreen}(2)${CClear} ${InvGreen} ${CClear} ${CGreen}(C)${CClear}onfiguration Menu / Main Setup Menu $rldisp${CClear}" echo -e "${InvGreen} ${CClear} Stop/Unmonitor VPN 1:${CGreen}(!)${CClear} 2:${CGreen}(@)${CClear} ${InvGreen} ${CClear} ${CGreen}(R)${CClear}eset VPN/WG CRON Time Scheduler: $schedtime" echo -e "${InvGreen} ${CClear} Enable/Disable ${CGreen}(M)${CClear}onitored VPN Slots ${InvGreen} ${CClear} ${CGreen}(L)${CClear}og Viewer / Trim Log Size (rows): $logSizeStr" echo -e "${InvGreen} ${CClear} Update/Maintain ${CGreen}(V)${CClear}PN Server Lists ${InvGreen} ${CClear} ${CGreen}(A)${CClear}utostart VPNMON-R3 on Reboot: $rebootprot" @@ -5404,7 +5768,7 @@ displayopsmenu() elif [ "$availableslots" = "1 2 3 4 5" ] then echo -e "${InvGreen} ${InvDkGray}${CWhite} Operations Menu ${CClear}" - echo -e "${InvGreen} ${CClear} Reset/Reconnect VPN 1:${CGreen}(1)${CClear} 2:${CGreen}(2)${CClear} 3:${CGreen}(3)${CClear} 4:${CGreen}(4)${CClear} 5:${CGreen}(5)${CClear} ${InvGreen} ${CClear} ${CGreen}(C)${CClear}onfiguration Menu / Main Setup Menu | $rldisp${CClear}" + echo -e "${InvGreen} ${CClear} Reset/Reconnect VPN 1:${CGreen}(1)${CClear} 2:${CGreen}(2)${CClear} 3:${CGreen}(3)${CClear} 4:${CGreen}(4)${CClear} 5:${CGreen}(5)${CClear} ${InvGreen} ${CClear} ${CGreen}(C)${CClear}onfiguration Menu / Main Setup Menu $rldisp${CClear}" echo -e "${InvGreen} ${CClear} Stop/Unmonitor VPN 1:${CGreen}(!)${CClear} 2:${CGreen}(@)${CClear} 3:${CGreen}(#)${CClear} 4:${CGreen}($)${CClear} 5:${CGreen}(%)${CClear} ${InvGreen} ${CClear} ${CGreen}(R)${CClear}eset VPN/WG CRON Time Scheduler: $schedtime" echo -e "${InvGreen} ${CClear} Reset/Reconnect WG 1:${CGreen}(6)${CClear} 2:${CGreen}(7)${CClear} 3:${CGreen}(8)${CClear} 4:${CGreen}(9)${CClear} 5:${CGreen}(0)${CClear} ${InvGreen} ${CClear} ${CGreen}(L)${CClear}og Viewer / Trim Log Size (rows): $logSizeStr" echo -e "${InvGreen} ${CClear} Stop/Unmonitor WG 1:${CGreen}(^)${CClear} 2:${CGreen}(&)${CClear} 3:${CGreen}(-)${CClear} 4:${CGreen}(+)${CClear} 5:${CGreen}(=)${CClear} ${InvGreen} ${CClear} ${CGreen}(A)${CClear}utostart VPNMON-R3 on Reboot: $rebootprot" @@ -6024,7 +6388,7 @@ do #if "$firstDataCollection" ; then printf "\r\033[0K" ; firstDataCollection=false ; fi # Print the results of all data gathered sofar # - echo -e "$wgindicator${InvDkGray}${CWhite} WG$i${CClear} | $wgmonitored | $wgservercnt | $wghealth | $wgstate | $wgip | $wgsvrping | $wgcity$wgsincelastreset $wgcitychange" + echo -e "$wgindicator${InvDkGray}${CWhite} WG$i${CClear} | $wgmonitored | $wgservercnt | $wghealth | $wgstate | $wgip | $wgsvrping | $wgcity$wgsincelastreset $wgcitychange$ubsync" #if a wg connection is monitored and disconnected, try to restart it if [ "$((WG$i))" = "1" ] && [ "$wgstate" = "Disconnected" ] @@ -6184,29 +6548,61 @@ do prevHideOpts=X #if Unbound is active and out of sync, try to restart it - if [ "$unboundclient" != "0" ] && [ "$ResolverTimer" = "1" ] && [ "$useovpn" = "1" ] + if [ "$unboundclient" != "0" ] && [ "$ResolverTimer" = "1" ] then - printf "\33[2K\r" - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: VPN$unboundreset is out of sync with Unbound DNS Resolver" >> $logfile - printf "${CGreen}\r[Unbound is out of sync]" - sleep 3 - printf "\33[2K\r" - #display a standard timer - timer=0 - while [ $timer -ne 5 ] - do - timer="$((timer+1))" - preparebar 46 "|" - progressbarpause $timer 5 "" "s" "Standard" - done - printf "\33[2K\r" + if [ "$useovpn" = "1" ] + then + printf "\33[2K\r" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: VPN$unboundreset is out of sync with Unbound DNS Resolver" >> $logfile + printf "${CGreen}\r[Unbound is out of sync]" + sleep 3 + printf "\33[2K\r" - restartvpn $unboundreset - sendmessage 1 "VPN Slot Not Synced With Unbound" $unboundreset - restartrouting - resetspdmerlin - exec sh /jffs/scripts/vpnmon-r3.sh -noswitch + #display a standard timer + timer=0 + while [ $timer -ne 5 ] + do + timer="$((timer+1))" + preparebar 46 "|" + progressbarpause $timer 5 "" "s" "Standard" + done + printf "\33[2K\r" + + restartvpn $unboundreset + sendmessage 1 "VPN Slot Not Synced With Unbound" $unboundreset + restartrouting + resetspdmerlin + exec sh /jffs/scripts/vpnmon-r3.sh -noswitch + fi + fi + + if [ "$unboundwgclient" != "0" ] && [ "$ResolverTimer" = "1" ] + then + if [ "$usewg" = "1" ] + then + printf "\33[2K\r" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WG$unboundreset is out of sync with Unbound DNS Resolver" >> $logfile + printf "${CGreen}\r[Unbound is out of sync]" + sleep 3 + printf "\33[2K\r" + + #display a standard timer + timer=0 + while [ $timer -ne 5 ] + do + timer="$((timer+1))" + preparebar 46 "|" + progressbarpause $timer 5 "" "s" "Standard" + done + printf "\33[2K\r" + + restartwg $unboundreset + sendmessage 1 "WG Slot Not Synced With Unbound" $unboundreset + restartrouting + resetspdmerlin + exec sh /jffs/scripts/vpnmon-r3.sh -noswitch + fi fi #Check to see if the WAN is up From b1748ca4c592148111e8a475dd1f40ec6cdd4892 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Mon, 11 Aug 2025 14:58:31 +0000 Subject: [PATCH 08/48] Rename vpnmon-r3-1.4.1.sh to archive/vpnmon-r3-1.4.1.sh --- vpnmon-r3-1.4.1.sh => archive/vpnmon-r3-1.4.1.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename vpnmon-r3-1.4.1.sh => archive/vpnmon-r3-1.4.1.sh (100%) diff --git a/vpnmon-r3-1.4.1.sh b/archive/vpnmon-r3-1.4.1.sh similarity index 100% rename from vpnmon-r3-1.4.1.sh rename to archive/vpnmon-r3-1.4.1.sh From d3a6dddea2c1cecf179d8f5564726e3a2d06427f Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:00:16 +0000 Subject: [PATCH 09/48] Create vpnmon-r3-1.5.0.sh --- vpnmon-r3-1.5.0.sh | 6223 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 6223 insertions(+) create mode 100644 vpnmon-r3-1.5.0.sh diff --git a/vpnmon-r3-1.5.0.sh b/vpnmon-r3-1.5.0.sh new file mode 100644 index 0000000..5c109ba --- /dev/null +++ b/vpnmon-r3-1.5.0.sh @@ -0,0 +1,6223 @@ +#!/bin/sh + +# VPNMON-R3 v1.5.0 (VPNMON-R3.SH) is an all-in-one script that is optimized to maintain multiple VPN connections and is +# able to provide for the capabilities to randomly reconnect using a specified server list containing the servers of your +# choice. Special care has been taken to ensure that only the VPN connections you want to have monitored are tended to. +# This script will check the health of up to 5 VPN connections on a regular interval to see if monitored VPN conenctions +# are connected, and sends a ping to a host of your choice through each active connection. If it finds that a connection +# has been lost, it will execute a series of commands that will kill that single VPN client, and randomly picks one of +# your specified servers to reconnect to for each VPN client. +# Last Modified: 2025-Aug-3 +########################################################################################## + +#Preferred standard router binaries path +export PATH="/sbin:/bin:/usr/sbin:/usr/bin:$PATH" + +#Static Variables - please do not change +version="1.5.0" # Version tracker +beta=0 # Beta switch +screenshotmode=0 # Switch to present bogus info for screenshots +apppath="/jffs/scripts/vpnmon-r3.sh" # Static path to the app +logfile="/jffs/addons/vpnmon-r3.d/vpnmon-r3.log" # Static path to the log +dlverpath="/jffs/addons/vpnmon-r3.d/version.txt" # Static path to the version file +config="/jffs/addons/vpnmon-r3.d/vpnmon-r3.cfg" # Static path to the config file +lockfile="/jffs/addons/vpnmon-r3.d/resetlock.txt" # Static path to the reset lock file +vr3emails="/jffs/addons/vpnmon-r3.d/vr3emails.txt" # Static path to email rate limit file +availableslots="1 2 3 4 5" # Available slots tracker +logsize=2000 # Log file size in rows +timerloop=60 # Timer loop in sec +schedule=0 # Scheduler enable y/n +schedulehrs=1 # Scheduler hours +schedulemin=0 # Scheduler mins +autostart=0 # Auto start on router reboot y/n +unboundclient=0 # Unbound bound to VPN client slot# +unboundshowip=0 # Show expanded Unbound IP on UI +ResolverTimer=1 # Timer to give DNS resolver time to settle +vpnping=0 # Tracking VPN Tunnel Pings +refreshserverlists=0 # Tracking Automated Custom VPN Server List Reset +monitorwan=0 # Tracking WAN/Dual WAN Monitoring +useovpn=1 # Tracking OVPN Display/Monitoring +usewg=1 # Tracking WG Display/Monitoring +lockactive=0 # Check for active locks +bypassscreentimer=0 # Check to see if screen timer can be bypassed +pingreset=500 # Maximum ping in ms before reset +updateskynet=0 # Check for VPN IP whitelisting in Skynet +amtmemailsuccess=0 # AMTM Email Success Message Option +amtmemailfailure=0 # AMTM Email Failure Message Option +rstspdmerlin=0 # Reset spdMerlin Interfaces Option +timeoutcmd="" # For "timeout" cmd for "nvram" calls +timeoutsec="" # For "timeout" cmd for "nvram" calls +timeoutlng="" # For "timeout" cmd for "nvram" calls +hideoptions=1 # Hide/Show menu options flag +ratelimit=0 # Rate limiting number of emails/houre + +##-------------------------------------## +## Added by Martinski W. [2024-Oct-05] ## +##-------------------------------------## +readonly PING_HOST_Deflt="8.8.8.8" +readonly IPv4octet_RegEx="([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])" +readonly IPv4addrs_RegEx="(${IPv4octet_RegEx}\.){3}${IPv4octet_RegEx}" +LAN_HostName="" +prevHideOpts=X # Avoid redisplaying the menu options unnecessarily too often # + +PINGHOST="$PING_HOST_Deflt" # Ping host + +## Custom Email Library Notification Variables ## +readonly scriptFileName="${0##*/}" +readonly scriptFileNTag="${scriptFileName%.*}" +readonly CEM_LIB_TAG="master" +readonly CEM_LIB_URL="https://raw.githubusercontent.com/Martinski4GitHub/CustomMiscUtils/${CEM_LIB_TAG}/EMail" +readonly CUSTOM_EMAIL_LIBDir="/jffs/addons/shared-libs" +readonly CUSTOM_EMAIL_LIBName="CustomEMailFunctions.lib.sh" +readonly CUSTOM_EMAIL_LIBFile="${CUSTOM_EMAIL_LIBDir}/$CUSTOM_EMAIL_LIBName" + +# Color variables +CBlack="\e[1;30m" +InvBlack="\e[1;40m" +CRed="\e[1;31m" +InvRed="\e[1;41m" +CGreen="\e[1;32m" +InvGreen="\e[1;42m" +CDkGray="\e[1;90m" +InvDkGray="\e[1;100m" +InvLtGray="\e[1;47m" +CYellow="\e[1;33m" +InvYellow="\e[1;43m" +CBlue="\e[1;34m" +InvBlue="\e[1;44m" +CMagenta="\e[1;35m" +CCyan="\e[1;36m" +InvCyan="\e[1;46m" +CWhite="\e[1;37m" +InvWhite="\e[1;107m" +CClear="\e[0m" + +# ------------------------------------------------------------------------------------------------------------------------- +# blackwhite is a simple function that removes all color attributes +blackwhite() +{ + +CBlack="" +InvBlack="" +CRed="" +InvRed="" +CGreen="" +InvGreen="" +CDkGray="" +InvDkGray="" +InvLtGray="" +CYellow="" +InvYellow="" +CBlue="" +InvBlue="" +CMagenta="" +CCyan="" +InvCyan="" +CWhite="" +InvWhite="" +CClear="" + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# LogoNM is a function that displays the VPNMON-R3 script name in a cool ASCII font without menu options + +logoNM () { + clear + echo "" + echo "" + echo "" + echo -e "${CDkGray} _ ______ _ ____ _______ _ __ ____ _____" + echo -e " | | / / __ \/ | / / |/ / __ \/ | / / / __ \__ /" + echo -e " | | / / /_/ / |/ / /|_/ / / / / |/ /_____/ /_/ //_ < " + echo -e " | |/ / ____/ /| / / / / /_/ / /| /_____/ _, _/__/ / " + echo -e " |___/_/ /_/ |_/_/ /_/\____/_/ |_/ /_/ |_/____/ v$version" + echo "" + echo "" + printf "\r ${CGreen} [ INITIALIZING ] ${CClear}" + sleep 2 + clear + echo "" + echo "" + echo "" + echo -e "${CYellow} _ ______ _ ____ _______ _ __ ____ _____" + echo -e " | | / / __ \/ | / / |/ / __ \/ | / / / __ \__ /" + echo -e " | | / / /_/ / |/ / /|_/ / / / / |/ /_____/ /_/ //_ < " + echo -e " | |/ / ____/ /| / / / / /_/ / /| /_____/ _, _/__/ / " + echo -e " |___/_/ /_/ |_/_/ /_/\____/_/ |_/ /_/ |_/____/ v$version" + echo "" + echo "" + printf "\r ${CGreen}[ INITIALIZING ... DONE ]${CClear}" + sleep 1 + printf "\r ${CGreen} [ LOADING... ] ${CClear}" + sleep 2 +} + +logoNMexit () { + clear + echo "" + echo "" + echo "" + echo -e "${CYellow} _ ______ _ ____ _______ _ __ ____ _____" + echo -e " | | / / __ \/ | / / |/ / __ \/ | / / / __ \__ /" + echo -e " | | / / /_/ / |/ / /|_/ / / / / |/ /_____/ /_/ //_ < " + echo -e " | |/ / ____/ /| / / / / /_/ / /| /_____/ _, _/__/ / " + echo -e " |___/_/ /_/ |_/_/ /_/\____/_/ |_/ /_/ |_/____/ v$version" + echo "" + echo "" + printf "\r ${CGreen} [ SHUTTING DOWN ] ${CClear}" + sleep 2 + clear + echo "" + echo "" + echo "" + echo -e "${CDkGray} _ ______ _ ____ _______ _ __ ____ _____" + echo -e " | | / / __ \/ | / / |/ / __ \/ | / / / __ \__ /" + echo -e " | | / / /_/ / |/ / /|_/ / / / / |/ /_____/ /_/ //_ < " + echo -e " | |/ / ____/ /| / / / / /_/ / /| /_____/ _, _/__/ / " + echo -e " |___/_/ /_/ |_/_/ /_/\____/_/ |_/ /_/ |_/____/ v$version" + echo "" + echo "" + printf "\r ${CGreen} [ SHUTTING DOWN ] ${CClear}" + sleep 1 + printf "\r ${CDkGray} [ GOODBYE... ] ${CClear}\n\n" + sleep 2 +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Promptyn is a simple function that accepts y/n input + +promptyn () { # No defaults, just y or n + while true; do + read -p '[y/n]? ' YESNO + case "$YESNO" in + [Yy]* ) return 0 ;; + [Nn]* ) return 1 ;; + * ) echo -e "\nPlease answer y or n.";; + esac + done +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Spinner is a script that provides a small indicator on the screen to show script activity + +spinner() +{ + spins=$1 + + spin=0 + totalspins=$((spins / 4)) + while [ $spin -le $totalspins ]; do + for spinchar in / - \\ \|; do + printf "\r$spinchar" + sleep 1 + done + spin=$((spin+1)) + done + + printf "\r" +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Preparebar and Progressbar is a script that provides a nice progressbar to show script activity +##----------------------------------------## +## Modified by Martinski W. [2024-Nov-01] ## +##----------------------------------------## +preparebar() +{ + barlen="$1" + barspaces="$(printf "%*s" "$1" ' ')" + barchars="$(printf "%*s" "$1" ' ' | tr ' ' "$2")" +} + +##----------------------------------------## +## Modified by Martinski W. [2024-Nov-02] ## +##----------------------------------------## +progressbaroverride() +{ + insertspc=" " + bypasswancheck=0 + + _GetPercent_() { printf "%.1f" "$(echo "$1" | awk "{print $1}")" ; } + + if [ "$1" -eq -1 ] + then + printf "\r $barspaces\r" + else + if [ $# -gt 6 ] && [ -n "$7" ] && [ "$1" -ge "$7" ] + then + barch="$(($7*barlen/$2))" + barsp="$((barlen-barch))" + percnt="$(_GetPercent_ "(100*$1/$2)")" + else + barch="$(($1*barlen/$2))" + barsp="$((barlen-barch))" + percnt="$(_GetPercent_ "(100*$1/$2)")" + fi + + if [ $# -gt 5 ] && [ -n "$6" ]; then AltNum="$6" ; else AltNum="$1" ; fi + + if [ "$5" = "Standard" ] + then + printf " ${CWhite}${InvDkGray}%3d${4} /%5.1f%%${CClear} [${CGreen}e${CClear}=Exit] [Selection? ${InvGreen} ${CClear}${CGreen}]\r${CClear}" "$AltNum" "$percnt" + fi + fi + + # Borrowed this wonderful keypress capturing mechanism from @Eibgrad... thank you! :) + key_press=''; read -rsn1 -t 1 key_press < "$(tty 0>&2)" + + if [ "$key_press" ] + then + case "$key_press" in + [1]) echo ""; restartvpn 1; sendmessage 0 "VPN Reset" 1; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [2]) echo ""; restartvpn 2; sendmessage 0 "VPN Reset" 2; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [3]) echo ""; restartvpn 3; sendmessage 0 "VPN Reset" 3; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [4]) echo ""; restartvpn 4; sendmessage 0 "VPN Reset" 4; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [5]) echo ""; restartvpn 5; sendmessage 0 "VPN Reset" 5; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [6]) echo ""; restartwg 1; sendmessage 0 "WG Reset" 1; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [7]) echo ""; restartwg 2; sendmessage 0 "WG Reset" 2; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [8]) echo ""; restartwg 3; sendmessage 0 "WG Reset" 3; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [9]) echo ""; restartwg 4; sendmessage 0 "WG Reset" 4; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [0]) echo ""; restartwg 5; sendmessage 0 "WG Reset" 5; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\!]) echo ""; killunmonvpn 1; sendmessage 0 "VPN Killed" 1; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\@]) echo ""; killunmonvpn 2; sendmessage 0 "VPN Killed" 2; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\#]) echo ""; killunmonvpn 3; sendmessage 0 "VPN Killed" 3; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\$]) echo ""; killunmonvpn 4; sendmessage 0 "VPN Killed" 4; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\%]) echo ""; killunmonvpn 5; sendmessage 0 "VPN Killed" 5; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\^]) echo ""; killunmonwg 1; sendmessage 0 "WG Killed" 1; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\&]) echo ""; killunmonwg 2; sendmessage 0 "WG Killed" 2; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\-]) echo ""; killunmonwg 3; sendmessage 0 "WG Killed" 3; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\+]) echo ""; killunmonwg 4; sendmessage 0 "WG Killed" 4; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\=]) echo ""; killunmonwg 5; sendmessage 0 "WG Killed" 5; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [Aa]) autostart;; + [Cc]) vsetup;; + [Dd]) wgserverlistautomation;; + [Ee]) logoNMexit; echo -e "${CClear}\n"; exit 0;; + [Hh]) hideoptions=1 ; [ "$hideoptions" != "$prevHideOpts" ] && timerreset=1 ;; + [Ii]) amtmevents;; + [Ll]) vlogs;; + [Mm]) vpnslots;; + [Pp]) maxping;; + [Rr]) schedulevpnreset;; + [Ss]) hideoptions=0 ; [ "$hideoptions" != "$prevHideOpts" ] && timerreset=1 ;; + [Tt]) timerloopconfig;; + [Uu]) vpnserverlistautomation;; + [Vv]) vpnserverlistmaint;; + [Ww]) wgserverlistmaint;; + [Xx]) uninstallr2;; + *) ;; ##IGNORE INVALID key presses ## + esac + bypasswancheck=1 + fi +} + +progressbarpause() +{ + insertspc=" " + bypasswancheck=0 + + if [ "$1" -eq -1 ] + then + printf "\r $barspaces\r" + else + if [ $# -gt 6 ] && [ -n "$7" ] && [ "$1" -ge "$7" ] + then + barch="$(($7*barlen/$2))" + barsp="$((barlen-barch))" + progr="$((100*$1/$2))" + else + barch="$(($1*barlen/$2))" + barsp="$((barlen-barch))" + progr="$((100*$1/$2))" + fi + + if [ $# -gt 5 ] && [ -n "$6" ]; then AltNum="$6" ; else AltNum="$1" ; fi + + if [ "$5" = "Standard" ] + then + printf " ${CWhite}${InvDkGray}Continuing Reset in $AltNum/5...${CClear} [${CGreen}p${CClear}=Pause] [${CGreen}e${CClear}=Exit] [Selection? ${InvGreen} ${CClear}${CGreen}]\r${CClear}" "$barchars" "$barspaces" + fi + fi + + # Borrowed this wonderful keypress capturing mechanism from @Eibgrad... thank you! :) + key_press=''; read -rsn1 -t 1 key_press < "$(tty 0>&2)" + + if [ $key_press ] + then + case $key_press in + [Pp]) vpause;; + [Ee]) logoNMexit; echo -e "${CClear}\n"; exit 0;; + esac + bypasswancheck=1 + fi +} + +# ------------------------------------------------------------------------------------------------------------------------- +# This function optionally uninstalls VPNMON-R2 if still found present on local router + +uninstallr2() +{ + +if [ -f /jffs/scripts/vpnmon-r2.sh ] +then + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} VPNMON-R2 Uninstall Utility ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} This utility allows you to uninstall VPNMON-R2. NOTE: Please know that VPNMON-R2" + echo -e "${InvGreen} ${CClear} development is ceasing, and product support will be sunset in the very near future." + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} VPNMON-R2 was still found installed on your router. This menu gives you the option" + echo -e "${InvGreen} ${CClear} to launch (if needed) or uninstall the old legacy VPNMON-R2. Thank you!" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo "" + printf "${CClear}Please select? (${CGreen}u${CClear}=UNinstall R2, ${CGreen}r${CClear}=Run R2, ${CGreen}e${CClear}=Exit)" + read -p ": " SelectR2 + case $SelectR2 in + [Uu]) + echo -e "\n${CClear}Please type 'Y' to validate you wish to proceed with the uninstall of VPNMON-R2.${CClear}" + if promptyn "(y/n): " + then + echo -e "\n${CClear}Uninstalling VPNMON-R2 components...${CClear}" + + if [ "$(cat /jffs/addons/vpnmon-r2.d/vpnmon-r2.cfg | grep "UpdateUnbound" | cut -d '=' -f 2-)" = "1" ] + then + # Delete all additions made to files to enable Unbound over VPN functionality + echo "" + echo -e "\n${CClear}Unbinding Unbound from VPN..." + # Disable vpn functionality with Unbound + sh /jffs/addons/unbound/unbound_manager.sh vpn=disable >/dev/null 2>&1 + # Remove Unbound failsafe in post-mount + if [ -f /jffs/scripts/post-mount ]; then + sed -i -e '/vpn=disable/d' /jffs/scripts/post-mount >/dev/null 2>&1 + fi + # Remove Unbound VPN helper script in openvpn-event + if [ -f /jffs/scripts/openvpn-event ]; then + sed -i -e '/unbound_DNS_via_OVPN.sh/d' /jffs/scripts/openvpn-event >/dev/null 2>&1 + fi + # Remove RPDB Rules added to nat-start + if [ -f /jffs/scripts/nat-start ]; then + sed -i -e '/Added by vpnmon-r2/d' /jffs/scripts/nat-start >/dev/null 2>&1 + fi + # Remove the unbound_DNS_via_OVPN.sh file + if [ -f /jffs/addons/unbound/unbound_DNS_via_OVPN.sh ]; then + rm /jffs/addons/unbound/unbound_DNS_via_OVPN.sh + fi + echo -e "\n${CClear}If Unbound-over-VPN is enabled on VPNMON-R3, you may need to disable/re-enable" + echo -e "this feature under the setup/configuration menu again. Conflicts may have" + echo -e "arisen with both products having this feature enabled." + fi + + echo -e "\n${CClear}Removing VPNMON-R2 Files..." + # Remove R2 Files + rm -f -r /jffs/addons/vpnmon-r2.d + rm -f /jffs/scripts/vpnmon-r2.sh + if [ -f /jffs/scripts/post-mount ]; then + sed -i -e '/vpnmon-r2.sh/d' /jffs/scripts/post-mount >/dev/null 2>&1 + fi + echo -e "\n${CClear}VPNMON-R2 has been uninstalled...${CClear}" + echo "" + read -rsp $'Press any key to continue...\n' -n1 key + timer="$timerloop" + echo -e "${CClear}\n" + return + else + timer="$timerloop" + echo -e "${CClear}\n" + return + fi + ;; + + [Rr]) exec sh /jffs/scripts/vpnmon-r2.sh -setup; exit 0;; + [Ee]) timer="$timerloop" ; echo -e "${CClear}\n"; return;; + + esac +fi + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# This function presents an Operational Menu in a Pause state, usually during VPN slot errors which give you a 5 second +# opportunity to pause in order to make any particular changes that might help your situation + +vpause() +{ + +while true +do + if [ "$availableslots" = "1 2" ] + then + clear + displayopsmenu + printf "${CClear}Please select? (${CGreen}n${CClear}=UNpause, ${CGreen}e${CClear}=Exit)" + read -p ": " SelectSlot + case $SelectSlot in + [1]) echo ""; restartvpn 1; sendmessage 0 "VPN Reset" 1; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [2]) echo ""; restartvpn 2; sendmessage 0 "VPN Reset" 2; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\!]) echo ""; killunmonvpn 1; sendmessage 0 "VPN Killed" 1; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\@]) echo ""; killunmonvpn 2; sendmessage 0 "VPN Killed" 2; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [Aa]) autostart;; + [Cc]) vsetup;; + [Ee]) echo -e "${CClear}\n"; exit 0;; + [Ii]) amtmevents;; + [Ll]) vlogs;; + [Mm]) vpnslots;; + [Pp]) maxping;; + [Rr]) schedulevpnreset;; + [Tt]) timerloopconfig;; + [Uu]) vpnserverlistautomation;; + [Vv]) vpnserverlistmaint;; + [Nn]) exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + *) exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + esac + elif [ "$availableslots" = "1 2 3 4 5" ] + then + clear + displayopsmenu + printf "${CClear}Please select? (${CGreen}n${CClear}=UNpause, ${CGreen}e${CClear}=Exit)" + read -p ": " SelectSlot + case $SelectSlot in + [1]) echo ""; restartvpn 1; sendmessage 0 "VPN Reset" 1; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [2]) echo ""; restartvpn 2; sendmessage 0 "VPN Reset" 2; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [3]) echo ""; restartvpn 3; sendmessage 0 "VPN Reset" 3; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [4]) echo ""; restartvpn 4; sendmessage 0 "VPN Reset" 4; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [5]) echo ""; restartvpn 5; sendmessage 0 "VPN Reset" 5; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [6]) echo ""; restartwg 1; sendmessage 0 "WG Reset" 1; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [7]) echo ""; restartwg 2; sendmessage 0 "WG Reset" 2; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [8]) echo ""; restartwg 3; sendmessage 0 "WG Reset" 3; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [9]) echo ""; restartwg 4; sendmessage 0 "WG Reset" 4; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [0]) echo ""; restartwg 5; sendmessage 0 "WG Reset" 5; restartrouting; resetspdmerlin; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\!]) echo ""; killunmonvpn 1; sendmessage 0 "VPN Killed" 1; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\@]) echo ""; killunmonvpn 2; sendmessage 0 "VPN Killed" 2; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\#]) echo ""; killunmonvpn 3; sendmessage 0 "VPN Killed" 3; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\$]) echo ""; killunmonvpn 4; sendmessage 0 "VPN Killed" 4; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\%]) echo ""; killunmonvpn 5; sendmessage 0 "VPN Killed" 5; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\^]) echo ""; killunmonwg 1; sendmessage 0 "WG Killed" 1; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\&]) echo ""; killunmonwg 2; sendmessage 0 "WG Killed" 2; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\-]) echo ""; killunmonwg 3; sendmessage 0 "WG Killed" 3; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\+]) echo ""; killunmonwg 4; sendmessage 0 "WG Killed" 4; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [\=]) echo ""; killunmonwg 5; sendmessage 0 "WG Killed" 5; exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + [Aa]) autostart;; + [Cc]) vsetup;; + [Ee]) echo -e "${CClear}\n"; exit 0;; + [Ii]) amtmevents;; + [Ll]) vlogs;; + [Mm]) vpnslots;; + [Pp]) maxping;; + [Rr]) schedulevpnreset;; + [Tt]) timerloopconfig;; + [Uu]) vpnserverlistautomation;; + [Vv]) vpnserverlistmaint;; + [Ww]) wgserverlistmaint;; + [Nn]) exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + *) exec sh /jffs/scripts/vpnmon-r3.sh -noswitch;; + esac + fi +done +} + +# ------------------------------------------------------------------------------------------------------------------------- +# creates needed config files if they don't exist + +createconfigs() +{ + + # Fix older versions using incorrect client slots + if [ "$availableslots" = "1 2 3" ] + then + availableslots="1 2" + rm -f /jffs/addons/vpnmon-r3.d/vr3clients.txt + rm -f /jffs/addons/vpnmon-r3.d/vr3timers.txt + saveconfig + fi + + # Create initial vr3clients.txt & vr3timers.txt file + if [ ! -f /jffs/addons/vpnmon-r3.d/vr3clients.txt ] + then + if [ "$availableslots" = "1 2" ] + then + { echo 'VPN1=0' + echo 'VPN2=0' + } > /jffs/addons/vpnmon-r3.d/vr3clients.txt + + elif [ "$availableslots" = "1 2 3 4 5" ] + then + { echo 'VPN1=0' + echo 'VPN2=0' + echo 'VPN3=0' + echo 'VPN4=0' + echo 'VPN5=0' + echo 'WG1=0' + echo 'WG2=0' + echo 'WG3=0' + echo 'WG4=0' + echo 'WG5=0' + } > /jffs/addons/vpnmon-r3.d/vr3clients.txt + fi + fi + + if [ ! -f /jffs/addons/vpnmon-r3.d/vr3timers.txt ] + then + if [ "$availableslots" = "1 2" ] + then + { echo 'VPNTIMER1=0' + echo 'VPNTIMER2=0' + } > /jffs/addons/vpnmon-r3.d/vr3timers.txt + + elif [ "$availableslots" = "1 2 3 4 5" ] + then + { echo 'VPNTIMER1=0' + echo 'VPNTIMER2=0' + echo 'VPNTIMER3=0' + echo 'VPNTIMER4=0' + echo 'VPNTIMER5=0' + echo 'WGTIMER1=0' + echo 'WGTIMER2=0' + echo 'WGTIMER3=0' + echo 'WGTIMER4=0' + echo 'WGTIMER5=0' + } > /jffs/addons/vpnmon-r3.d/vr3timers.txt + fi + fi + + + if [ "$availableslots" = "1 2 3 4 5" ] + then + # Determine if the new Wireguard client values are present, if not, add them + if ! grep -q "WG" "/jffs/addons/vpnmon-r3.d/vr3clients.txt" + then + # Check for and remove empty lines after the last entry before the EOF + awk 'NF > 0 {for (i=1; i<=b; i++) print ""; b=0; print} NF==0 {b++}' "/jffs/addons/vpnmon-r3.d/vr3clients.txt" > "/jffs/addons/vpnmon-r3.d/vr3clients.tmp" + # Overwrite the original file with the cleaned-up version. + mv "/jffs/addons/vpnmon-r3.d/vr3clients.tmp" "/jffs/addons/vpnmon-r3.d/vr3clients.txt" + # Write the new Wireguard timer values + cat << EOF >> "/jffs/addons/vpnmon-r3.d/vr3clients.txt" +WG1=0 +WG2=0 +WG3=0 +WG4=0 +WG5=0 +EOF + + fi + + # Determine if the new Wireguard timer values are present, if not, add them + if ! grep -q "WGTIMER" "/jffs/addons/vpnmon-r3.d/vr3timers.txt" + then + # Check for and remove empty lines after the last entry before the EOF + awk 'NF > 0 {for (i=1; i<=b; i++) print ""; b=0; print} NF==0 {b++}' "/jffs/addons/vpnmon-r3.d/vr3timers.txt" > "/jffs/addons/vpnmon-r3.d/vr3timers.tmp" + # Overwrite the original file with the cleaned-up version. + mv "/jffs/addons/vpnmon-r3.d/vr3timers.tmp" "/jffs/addons/vpnmon-r3.d/vr3timers.txt" + # Write the new Wireguard timer values + cat << EOF >> "/jffs/addons/vpnmon-r3.d/vr3timers.txt" +WGTIMER1=0 +WGTIMER2=0 +WGTIMER3=0 +WGTIMER4=0 +WGTIMER5=0 +EOF + + fi + fi + +} + +##-------------------------------------## +## Added by Martinski W. [2024-Oct-05] ## +##-------------------------------------## +_SetUpTimeoutCmdVars_() +{ + # If the timeout utility is available then use it # + if [ -z "${timeoutcmd:+xSETx}" ] && [ -f "/opt/bin/timeout" ] + then + timeoutcmd="timeout " + timeoutsec="10" + timeoutlng="60" + fi +} + +##-------------------------------------## +## Added by Martinski W. [2024-Oct-05] ## +##-------------------------------------## +_SetLAN_HostName_() +{ + [ -z "${LAN_HostName:+xSETx}" ] && \ + LAN_HostName="$($timeoutcmd$timeoutsec nvram get lan_hostname)" +} + +_GetLAN_HostName_() +{ _SetLAN_HostName_ ; echo "$LAN_HostName" ; } + +# ------------------------------------------------------------------------------------------------------------------------- +# vsetup provide a menu interface to allow for initial component installs, uninstall, etc. + +##----------------------------------------## +## Modified by Martinski W. [2024-Oct-05] ## +##----------------------------------------## +vsetup() +{ + _SetUpTimeoutCmdVars_ + +while true +do + clear # Initial Setup + if [ ! -f "$config" ]; then # Write /jffs/addons/vpnmon-r3.d/vpnmon-r3.cfg + saveconfig + fi + + createconfigs + + echo -e "${InvGreen} ${InvDkGray}${CWhite} VPNMON-R3 Main Setup and Configuration Menu ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please choose from the various options below, which allow you to perform high level${CClear}" + echo -e "${InvGreen} ${CClear} actions in the management of the VPNMON-R3 script.${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(1)${CClear} : Custom configuration options for VPNMON-R3${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(2)${CClear} : Force reinstall Entware dependencies${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(3)${CClear} : Check for latest updates${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(4)${CClear} : Uninstall VPNMON-R3${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} | ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(e)${CClear} : Exit${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo "" + read -p "Please select? (1-4, e=Exit): " SelectSlot + case $SelectSlot in + 1) # Check for existence of entware, and if so proceed and install the timeout package, then run vpnmon-r3 -config + clear + if [ -f "/opt/bin/timeout" ] && [ -f "/opt/sbin/screen" ] && [ -f "/opt/bin/jq" ] + then + vconfig + else + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Install Dependencies ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Missing dependencies required by VPNMON-R3 will be installed during this process." + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo "" + echo -e "VPNMON-R3 has some dependencies in order to function correctly, namely, CoreUtils-Timeout" + echo -e "JQ, and the Screen utility. These utilities require you to have Entware already installed" + echo -e "using the AMTM tool. If Entware is present, the Timeout, JQ and Screen utilities will" + echo -e "automatically be downloaded and installed during this process." + echo "" + echo -e "${CGreen}CoreUtils-Timeout${CClear} is a utility that provides more stability for certain routers (like" + echo -e "the RT-AC86U) which has a tendency to randomly hang scripts running on this router model." + echo "" + echo -e "${CGreen}JQuery${CClear} is a utility for querying data across the internet through the the means of" + echo -e "APIs for the purposes of interacting with the various VPN providers to get a list" + echo -e "of available VPN hosts in the selected location(s)." + echo "" + echo -e "${CGreen}Screen${CClear} is a utility that allows you to run SSH scripts in a standalone environment" + echo -e "directly on the router itself, instead of running your commands or a script from a network-" + echo -e "attached SSH client. This can provide greater stability due to it running on the router" + echo -e "itself." + echo "" + echo -e "Your router model is: ${CGreen}$ROUTERMODEL${CClear}" + echo "" + echo -e "Ready to install?" + if promptyn " (y/n): " + then + if [ -d "/opt" ]; then # Does entware exist? If yes proceed, if no error out. + echo "" + echo -e "\n${CClear}Updating Entware Packages..." + echo "" + opkg update + echo "" + echo -e "Installing Entware ${CGreen}CoreUtils-Timeout${CClear} Package...${CClear}" + echo "" + opkg install coreutils-timeout + echo "" + echo -e "Installing Entware ${CGreen}JQuery${CClear} Package...${CClear}" + echo "" + opkg install jq + echo "" + echo -e "Installing Entware ${CGreen}Screen${CClear} Package...${CClear}" + echo "" + opkg install screen + echo "" + echo -e "Install completed..." + echo "" + read -rsp $'Press any key to continue...\n' -n1 key + echo "" + echo -e "Executing Configuration Utility..." + sleep 2 + vconfig + else + clear + echo -e "${CRed}ERROR: Entware was not found on this router...${CClear}" + echo -e "Please install Entware using the AMTM utility before proceeding..." + echo "" + read -rsp $'Press any key to continue...\n' -n1 key + fi + else + echo "" + echo -e "\nExecuting Configuration Utility..." + sleep 2 + vconfig + fi + fi + ;; + + 2) # Force re-install the CoreUtils timeout/screen package + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Re-install Dependencies ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Missing dependencies required by VPNMON-R3 will be re-installed during this process." + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo "" + echo -e "Would you like to re-install the CoreUtils-Timeout, JQ and the Screen utility? These" + echo -e "utilities require you to have Entware already installed using the AMTM tool. If Entware" + echo -e "is present, the Timeout and Screen utilities will be uninstalled, downloaded and re-" + echo -e "installed during this setup process..." + echo "" + echo -e "${CGreen}CoreUtils-Timeout${CClear} is a utility that provides more stability for certain routers (like" + echo -e "the RT-AC86U) which has a tendency to randomly hang scripts running on this router" + echo -e "model." + echo "" + echo -e "${CGreen}JQuery${CClear} is a utility for querying data across the internet through the the means of" + echo -e "APIs for the purposes of interacting with the various VPN providers to get a list" + echo -e "of available VPN hosts in the selected location(s)." + echo "" + echo -e "${CGreen}Screen${CClear} is a utility that allows you to run SSH scripts in a standalone environment" + echo -e "directly on the router itself, instead of running your commands or a script from a" + echo -e "network-attached SSH client. This can provide greater stability due to it running on" + echo -e "the router itself." + echo "" + echo -e "Your router model is: ${CGreen}$ROUTERMODEL${CClear}" + echo "" + echo -e "Force Re-install?" + if promptyn "[y/n]: " + then + if [ -d "/opt" ]; then # Does entware exist? If yes proceed, if no error out. + echo "" + echo -e "\nUpdating Entware Packages..." + echo "" + opkg update + echo "" + echo -e "Force Re-installing Entware ${CGreen}CoreUtils-Timeout${CClear} Package..." + echo "" + opkg install --force-reinstall coreutils-timeout + echo "" + echo -e "Force Re-installing Entware ${CGreen}JQuery${CClear} Package..." + echo "" + opkg install --force-reinstall . jq + echo "" + echo -e "Force Re-installing Entware ${CGreen}Screen${CClear} Package..." + echo "" + opkg install --force-reinstall screen + echo "" + echo -e "Re-install completed..." + echo "" + read -rsp $'Press any key to continue...\n' -n1 key + else + clear + echo -e "${CRed}ERROR: Entware was not found on this router...${CClear}" + echo -e "Please install Entware using the AMTM utility before proceeding..." + echo "" + read -rsp $'Press any key to continue...\n' -n1 key + fi + fi + ;; + 3) vupdate;; + 4) vuninstall;; + [Ee]) + echo "" + timer="$timerloop" + break;; + esac +done + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# vconfig is a function that provides a UI to choose various options for vpnmon-r3 + +##----------------------------------------## +## Modified by Martinski W. [2024-Oct-05] ## +##----------------------------------------## +vconfig() +{ + _SetUpTimeoutCmdVars_ + +# Grab the VPNMON-R3 config file and read it in +if [ -f "$config" ] +then + source "$config" +else + clear + echo -e "${CRed}ERROR: VPNMON-R3 is not configured. Please run 'vpnmon-r3.sh -setup' first." + echo "" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: VPNMON-R3 config file not found. Please run the setup/configuration utility" >> $logfile + echo -e "${CClear}" + exit 1 +fi + +while true +do + if [ "$availableslots" = "1 2" ]; then + availableslotsdisp="2 x VPN" + elif [ "$availableslots" = "1 2 3 4 5" ]; then + availableslotsdisp="5 x VPN | 5 x WG" + fi + + if [ "$unboundclient" -eq 0 ]; then + unboundclientexp="Disabled" + else + unboundclientexp="Enabled, VPN$unboundclient" + fi + + if [ "$unboundshowip" -eq 0 ]; then + unboundshowipdisp="Disabled" + else + unboundshowipdisp="Enabled" + fi + + if [ "$refreshserverlists" -eq 0 ]; then + refreshserverlistsdisp="Disabled" + else + refreshserverlistsdisp="Enabled" + fi + + if [ "$monitorwan" -eq 0 ]; then + monitorwandisp="Disabled" + else + monitorwandisp="Enabled" + fi + + if [ "$useovpn" -eq 0 ] && [ "$usewg" -eq 0 ]; then + useovpnwgDisp="${CRed}VPN/WG Disabled" + elif + [ "$useovpn" -eq 1 ] && [ "$usewg" -eq 0 ]; then + useovpnwgDisp="${CGreen}VPN Only" + elif + [ "$useovpn" -eq 0 ] && [ "$usewg" -eq 1 ]; then + useovpnwgDisp="${CGreen}WG Only" + elif + [ "$useovpn" -eq 1 ] && [ "$usewg" -eq 1 ]; then + useovpnwgDisp="${CGreen}VPN/WG Enabled" + fi + + if [ "$updateskynet" -eq 0 ]; then + updateskynetdisp="Disabled" + else + updateskynetdisp="Enabled" + fi + + if [ "$amtmemailsuccess" = "0" ] && [ "$amtmemailfailure" = "0" ]; then + amtmemailsuccfaildisp="Disabled" + elif [ "$amtmemailsuccess" = "1" ] && [ "$amtmemailfailure" = "0" ]; then + amtmemailsuccfaildisp="Success" + elif [ "$amtmemailsuccess" = "0" ] && [ "$amtmemailfailure" = "1" ]; then + amtmemailsuccfaildisp="Failure" + elif [ "$amtmemailsuccess" = "1" ] && [ "$amtmemailfailure" = "1" ]; then + amtmemailsuccfaildisp="Success, Failure" + else + amtmemailsuccfaildisp="Disabled" + fi + + if [ "$rstspdmerlin" -eq 0 ]; then + rstspdmerlindisp="Disabled" + else + rstspdmerlindisp="Enabled" + fi + + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} VPNMON-R3 Configuration Options ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please choose from the various options below, which allow you to modify certain${CClear}" + echo -e "${InvGreen} ${CClear} customizable parameters that affect the operation of this script.${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 1)${CClear} : Number of VPN/WG Client Slots available : ${CGreen}$availableslotsdisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 2)${CClear} : Custom PING host to determine VPN health : ${CGreen}$PINGHOST" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 3)${CClear} : Custom Event Log size (rows) : ${CGreen}$logsize" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 4)${CClear} : Unbound DNS Lookups over VPN Integration : ${CGreen}$unboundclientexp" + if [ "$unboundclient" -eq 0 ]; then + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 5)${CClear} -- ${CDkGray}Show Expanded Unbound IP Info? : $unboundshowipdisp" + else + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 5)${CClear} -- Show Expanded Unbound IP Info? : ${CGreen}$unboundshowipdisp" + fi + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 6)${CClear} : Refresh Custom Server Lists on -RESET Switch : ${CGreen}$refreshserverlistsdisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 7)${CClear} : Provide additional WAN/Dual WAN monitoring : ${CGreen}$monitorwandisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 8)${CClear} : Enable/Disable VPN/WG Slot Monitoring : $useovpnwgDisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( 9)${CClear} : Whitelist VPN Server IP Lists in Skynet : ${CGreen}$updateskynetdisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(10)${CClear} : AMTM Email Notifications on Success/Failure : ${CGreen}$amtmemailsuccfaildisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}(11)${CClear} : Reset spdMerlin Interfaces on VPN Reset : ${CGreen}$rstspdmerlindisp" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} | ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}( e)${CClear} : Exit${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo "" + read -p "Please select? (1-11, e=Exit): " SelectSlot + case $SelectSlot in + 1) + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Number of VPN/WG Client Slots Available on Router ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate how many VPN/WG client slots your router is configured with. Certain${CClear}" + echo -e "${InvGreen} ${CClear} older model routers (RT-AC68U) can only handle a maximum of 2 VPN client slots, and${CClear}" + echo -e "${InvGreen} ${CClear} natively can't handle WG without some effort using 3rd party scripts, while the vast${CClear}" + echo -e "${InvGreen} ${CClear} majority of newer models can handle 5 VPN and 5 WG slots. Easiest way to tell is by${CClear}" + echo -e "${InvGreen} ${CClear} looking at your VPN settings within the Merlin Web UI. Please choose below:" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${CGreen}(2)${CClear} = 2 x VPN slots (Older router models)" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${CGreen}(5)${CClear} = 5 x VPN | 5 x WG slots (Newer router models)" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} (Default = 5 VPN/WG client slots)${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo + echo -e "${CClear}Current: ${CGreen}$availableslotsdisp${CClear}" ; echo + read -p "Please enter value (2 or 5)? (e=Exit): " newAvailableSlots + if [ "$newAvailableSlots" = "2" ] + then + availableslots="1 2" + availableslotsdisp="2 x VPN" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Available VPN Client Slot Configuration saved as: $availableslotsdisp" >> $logfile + rm -f /jffs/addons/vpnmon-r3.d/vr3clients.txt + rm -f /jffs/addons/vpnmon-r3.d/vr3timers.txt + saveconfig + createconfigs + elif [ "$newAvailableSlots" = "5" ] + then + availableslots="1 2 3 4 5" + availableslotsdisp="5 x VPN | 5 x WG" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Available VPN Client Slot Configuration saved as: $availableslotsdisp" >> $logfile + rm -f /jffs/addons/vpnmon-r3.d/vr3clients.txt + rm -f /jffs/addons/vpnmon-r3.d/vr3timers.txt + saveconfig + createconfigs + elif [ "$newAvailableSlots" = "e" ] + then + echo -e "\n[Exiting]"; sleep 2 + else + previousValue="$availableslots" + availableslots="${availableslots:=1 2 3 4 5}" + availableslotsdisp="5 x VPN | 5 x WG" + [ "$availableslots" != "$previousValue" ] && \ + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Available VPN Client Slot Configuration saved as: $availableslotsdisp" >> $logfile + rm -f /jffs/addons/vpnmon-r3.d/vr3clients.txt + rm -f /jffs/addons/vpnmon-r3.d/vr3timers.txt + saveconfig + createconfigs + fi + ;; + + 2) + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Custom PING Host (to determine VPN health) ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate which host you want to PING in order to determine VPN Client health.${CClear}" + echo -e "${InvGreen} ${CClear} By default, the script will ping $PING_HOST_Deflt (Google DNS) as it's reliable, fairly${CClear}" + echo -e "${InvGreen} ${CClear} standard, and typically available globally. You can change this depending on your" + echo -e "${InvGreen} ${CClear} local access and connectivity situation." + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} (Default = ${PING_HOST_Deflt})${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo + echo -e "${CClear}Current: ${CGreen}${PINGHOST}${CClear}" ; echo + read -p "Please enter valid IPv4 address? (e=Exit): " newPingHost + if [ "$newPingHost" = "e" ] + then + echo -e "\n[Exiting]"; sleep 2 + elif [ -n "$newPingHost" ] && echo "$newPingHost" | grep -qE "^${IPv4addrs_RegEx}$" + then + PINGHOST="$newPingHost" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New custom PING host entered: $PINGHOST" >> $logfile + saveconfig + else + previousValue="$PINGHOST" + PINGHOST="${PINGHOST:=$PING_HOST_Deflt}" + [ "$PINGHOST" != "$previousValue" ] && \ + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New custom PING host entered: $PINGHOST" >> $logfile + saveconfig + fi + ;; + + 3) + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Custom Event Log Size ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate below how large you would like your Event Log to grow. I'm a poet${CClear}" + echo -e "${InvGreen} ${CClear} and didn't even know it. By default, with 2000 rows, you will have many months of${CClear}" + echo -e "${InvGreen} ${CClear} Event Log data." + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Use 0 to Disable, max number of rows is 9999. (Default = 2000)" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo + echo -e "${CClear}Current: ${CGreen}$logsize${CClear}" ; echo + read -p "Please enter Log Size (in rows)? (0-9999, e=Exit): " newLogSize + if [ "$newLogSize" = "e" ] + then + echo -e "\n[Exiting]"; sleep 2 + elif echo "$newLogSize" | grep -qE "^(0|[1-9][0-9]{0,3})$" && \ + [ "$newLogSize" -ge 0 ] && [ "$newLogSize" -le 9999 ] + then + logsize="$newLogSize" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New custom Event Log Size entered (in rows): $logsize" >> $logfile + saveconfig + else + previousValue="$logsize" + logsize="${logsize:=2000}" + [ "$logsize" != "$previousValue" ] && \ + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New custom Event Log Size entered (in rows): $logsize" >> $logfile + saveconfig + fi + ;; + + 4) + while true; do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Unbound DNS Lookups over VPN Integration ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate if you would like to enable an integration with Unbound which allows${CClear}" + echo -e "${InvGreen} ${CClear} you to force any DNS lookups that Unbound would normally handle, but over your VPN${CClear}" + echo -e "${InvGreen} ${CClear} connection, essentially allowing you to appear as your own DNS resolver. DNS resolver${CClear}" + echo -e "${InvGreen} ${CClear} traffic generated by Unbound is unencrypted as it queries DNS Root Servers by default.${CClear}" + echo -e "${InvGreen} ${CClear} This integration encrypts your DNS Resolver traffic up to your Public VPN IP address,${CClear}" + echo -e "${InvGreen} ${CClear} after which it goes unencrypted to the DNS Root Servers, preventing any ISP or other${CClear}" + echo -e "${InvGreen} ${CClear} traffic inspection snooping when sending this across your WAN connection.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} IMPORTANT: This integration will only work with 1 primary VPN connection that is${CClear}" + echo -e "${InvGreen} ${CClear} designated as the connection that will carry DNS resolution traffic. This integration${CClear}" + echo -e "${InvGreen} ${CClear} will work when multiple VPN connections are running, but the other VPN connections may${CClear}" + echo -e "${InvGreen} ${CClear} not be able to utilize the Unbound functionality, and considered standalone. You will${CClear}" + echo -e "${InvGreen} ${CClear} need to identify one VPN Client Slot that will remain on at all times. This integration${CClear}" + echo -e "${InvGreen} ${CClear} requires additional scripts and configurations to get it working correctly. Please know${CClear}" + echo -e "${InvGreen} ${CClear} that multiple files will either be downloaded or modified, and may increase the${CClear}" + echo -e "${InvGreen} ${CClear} complexity in troubleshooting DNS lookup issues.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${CRed}WARNING: As of around March 1 2024, the Unbound integration no longer works with the${CClear}" + echo -e "${InvGreen} ${CClear} ${CRed}NordVPN Service due to possible blocking on their end. This service continues to work${CClear}" + echo -e "${InvGreen} ${CClear} ${CRed}as advertised with other VPN Providers, like AirVPN. Use at your own risk.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${CGreen}Requirements:" + echo -e "${InvGreen} ${CClear} ${CGreen}1. Unbound must already be installed and functioning (AMTM)" + echo -e "${InvGreen} ${CClear} ${CGreen}2. One static VPN Slot must be designated for Unbound-over-VPN use" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${CGreen}These files will be downloaded or modified:" + echo -e "${InvGreen} ${CClear} ${CGreen}1. /jffs/scripts/nat-start (modified)" + echo -e "${InvGreen} ${CClear} ${CGreen}2. /jffs/scripts/openvpn-event (modified)" + echo -e "${InvGreen} ${CClear} ${CGreen}3. /jffs/scripts/post-mount (modified)" + echo -e "${InvGreen} ${CClear} ${CGreen}4. /jffs/addons/unbound/unbound_DNS_via_OVPN.sh (downloaded)" + echo -e "${InvGreen} ${CClear} ${CGreen}5. Required router reboot - please don't forget to do so!${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} (Default = 0 / Disabled)${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo "" + if [ $unboundclient -eq 0 ]; then + unboundclientexp="Disabled" + else + unboundclientexp="Enabled, VPN$unboundclient" + fi + + echo -e "${CClear}Current: ${CGreen}$unboundclientexp${CClear}" + echo "" + read -p "Please enter value (Disabled=0, VPN Client=1-5)? (e=Exit): " unboundovervpn + + if [ ! -d "/jffs/addons/unbound" ]; then + echo "" + echo -e "${CRed}[Unbound was not detected on this system. Exiting...]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: Unbound was not detected on this system." >> $logfile + unboundclient=0 + saveconfig + sleep 3 + break + fi + + if [ "$unboundovervpn" = "0" ] || [ "$unboundovervpn" = "1" ] || [ "$unboundovervpn" = "2" ] || [ "$unboundovervpn" = "3" ] || [ "$unboundovervpn" = "4" ] || [ "$unboundovervpn" = "5" ] || [ "$unboundovervpn" = "e" ]; then + if [ "$unboundovervpn" = "0" ]; then + + # Delete all additions made to files to enable Unbound over VPN functionality + echo "" + echo -e "${CGreen}[Disabling Unbound over VPN]...${CClear}" + + # Disable vpn functionality with Unbound + sh /jffs/addons/unbound/unbound_manager.sh vpn=disable >/dev/null 2>&1 + + # Remove Unbound failsafe in post-mount + if [ -f /jffs/scripts/post-mount ]; then + sed -i -e '/vpn=disable/d' /jffs/scripts/post-mount + fi + + # Remove Unbound VPN helper script in openvpn-event + if [ -f /jffs/scripts/openvpn-event ]; then + sed -i -e '/unbound_DNS_via_OVPN.sh/d' /jffs/scripts/openvpn-event + fi + + # Remove RPDB Rules added to nat-start + if [ -f /jffs/scripts/nat-start ]; then + sed -i -e '/Added by vpnmon/d' /jffs/scripts/nat-start + fi + + # Remove the unbound_DNS_via_OVPN.sh file + if [ -f /jffs/addons/unbound/unbound_DNS_via_OVPN.sh ]; then + rm -f /jffs/addons/unbound/unbound_DNS_via_OVPN.sh + fi + + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Unbound-over-VPN was removed from VPNMON-R3" >> $logfile + unboundclient=0 + saveconfig + sleep 3 + break + + elif [ "$unboundovervpn" = "1" ] || [ "$unboundovervpn" = "2" ] || [ "$unboundovervpn" = "3" ] || [ "$unboundovervpn" = "4" ] || [ "$unboundovervpn" = "5" ]; then + + if [ "$unboundovervpn" = "$unboundclient" ]; then + echo -e "${CClear}\n[Unbound over VPN$unboundovervpn Already Active]"; sleep 2; break + fi + + if [ "$unboundovervpn" = "1" ] || [ "$unboundovervpn" = "2" ] || [ "$unboundovervpn" = "3" ] || [ "$unboundovervpn" = "4" ] || [ "$unboundovervpn" = "5" ] && [ $unboundclient -ge 1 ]; then + echo "" + echo -e "${CRed}When changing a VPN Client Slot (from Slot #$unboundclient to Slot #$unboundovervpn), please proceed to 'Disable'" + echo -e "first (option 0), then choose a new VPN Slot." + sleep 5; break + fi + + # Modify or create post-mount + if [ -f /jffs/scripts/post-mount ]; then + + if ! grep -q -F "[ -n "'"$(which unbound_manager)"'" ] && { sh /jffs/addons/unbound/unbound_manager.sh vpn=disable; }" /jffs/scripts/post-mount; then + echo "[ -n "'"$(which unbound_manager)"'" ] && { sh /jffs/addons/unbound/unbound_manager.sh vpn=disable; } # Added by vpnmon-r3" >> /jffs/scripts/post-mount + fi + + else + echo "#!/bin/sh" > /jffs/scripts/post-mount + echo "" >> /jffs/scripts/post-mount + echo "[ -n "'"$(which unbound_manager)"'" ] && { sh /jffs/addons/unbound/unbound_manager.sh vpn=disable; } # Added by vpnmon-r3" >> /jffs/scripts/post-mount + chmod 755 /jffs/scripts/post-mount + fi + + # Modify or create nat-start + if [ -f /jffs/scripts/nat-start ]; then + + if ! grep -q -F "sleep 10 # During the boot process nat-start may run multiple times so this is required" /jffs/scripts/nat-start; then + echo "" >> /jffs/scripts/nat-start + echo "sleep 10 # During the boot process nat-start may run multiple times so this is required - Added by vpnmon-r3" >> /jffs/scripts/nat-start + echo "" >> /jffs/scripts/nat-start + echo "# Ensure duplicate rules are not created - Added by vpnmon-r3" >> /jffs/scripts/nat-start + echo "for VPN_ID in 0 1 2 3 4 5 # Added by vpnmon-r3" >> /jffs/scripts/nat-start + echo " do # Added by vpnmon-r3" >> /jffs/scripts/nat-start + echo " ip rule del prio 999$VPN_ID 2>/dev/null # Added by vpnmon-r3" >> /jffs/scripts/nat-start + echo " done # Added by vpnmon-r3" >> /jffs/scripts/nat-start + echo "" >> /jffs/scripts/nat-start + echo "# Create the RPDB rules - Added by vpnmon-r3" >> /jffs/scripts/nat-start + echo "ip rule add from 0/0 fwmark \"0x8000/0x8000\" table main prio 9990 # WAN fwmark - Added by vpnmon-r3" >> /jffs/scripts/nat-start + echo "ip rule add from 0/0 fwmark \"0x7000/0x7000\" table ovpnc4 prio 9991 # VPN 4 fwmark - Added by vpnmon-r3" >> /jffs/scripts/nat-start + echo "ip rule add from 0/0 fwmark \"0x3000/0x3000\" table ovpnc5 prio 9992 # VPN 5 fwmark - Added by vpnmon-r3" >> /jffs/scripts/nat-start + echo "ip rule add from 0/0 fwmark \"0x1000/0x1000\" table ovpnc1 prio 9993 # VPN 1 fwmark - Added by vpnmon-r3" >> /jffs/scripts/nat-start + echo "ip rule add from 0/0 fwmark \"0x2000/0x2000\" table ovpnc2 prio 9994 # VPN 2 fwmark - Added by vpnmon-r3" >> /jffs/scripts/nat-start + echo "ip rule add from 0/0 fwmark \"0x4000/0x4000\" table ovpnc3 prio 9995 # VPN 3 fwmark - Added by vpnmon-r3" >> /jffs/scripts/nat-start + fi + + else + curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 2 --retry-all-errors "https://raw.githubusercontent.com/ViktorJp/VPNMON-R2/main/Unbound/nat-start" -o "/jffs/scripts/nat-start" && chmod 755 "/jffs/scripts/nat-start" + fi + + # Modify or create openvpn-event + if [ -f /jffs/scripts/openvpn-event ]; then + + if ! grep -q -F "[ "'"${dev:0:4}"'" = 'tun1' ] && vpn_id" /jffs/scripts/openvpn-event; then + echo "[ "'"${dev:0:4}"'" = 'tun1' ] && vpn_id=$unboundovervpn && [ "'"$script_type"'" = 'route-up' ] && /jffs/addons/unbound/unbound_DNS_via_OVPN.sh \$vpn_id start &" >> /jffs/scripts/openvpn-event + echo "[ "'"${dev:0:4}"'" = 'tun1' ] && vpn_id=$unboundovervpn && [ "'"$script_type"'" = 'route-pre-down' ] && /jffs/addons/unbound/unbound_DNS_via_OVPN.sh \$vpn_id stop &" >> /jffs/scripts/openvpn-event + fi + + else + echo "#!/bin/sh" > /jffs/scripts/openvpn-event + echo "" >> /jffs/scripts/openvpn-event + echo "[ "'"${dev:0:4}"'" = 'tun1' ] && vpn_id=$unboundovervpn && [ "'"$script_type"'" = 'route-up' ] && /jffs/addons/unbound/unbound_DNS_via_OVPN.sh \$vpn_id start &" >> /jffs/scripts/openvpn-event + echo "[ "'"${dev:0:4}"'" = 'tun1' ] && vpn_id=$unboundovervpn && [ "'"$script_type"'" = 'route-pre-down' ] && /jffs/addons/unbound/unbound_DNS_via_OVPN.sh \$vpn_id stop &" >> /jffs/scripts/openvpn-event + chmod 755 /jffs/scripts/openvpn-event + # backup - curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 2 --retry-all-errors "https://raw.githubusercontent.com/ViktorJp/VPNMON-R2/main/Unbound/openvpn-event" -o "/jffs/scripts/openvpn-event" && chmod 755 "/jffs/scripts/openvpn-event" + fi + + # Download and create the unbound_DNS_via_OVPN.sh file - many thanks to @Martineau and @Swinson + if [ ! -f /jffs/addons/unbound/unbound_DNS_via_OVPN.sh ]; then + curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 2 --retry-all-errors "https://raw.githubusercontent.com/MartineauUK/Unbound-Asuswrt-Merlin/dev/unbound_DNS_via_OVPN.sh" -o "/jffs/addons/unbound/unbound_DNS_via_OVPN.sh" && chmod 755 "/jffs/addons/unbound/unbound_DNS_via_OVPN.sh" + # backup - curl --silent --retry 3 "https://raw.githubusercontent.com/ViktorJp/VPNMON-R2/main/Unbound/unbound_DNS_via_OVPN.sh" -o "/jffs/addons/unbound/unbound_DNS_via_OVPN.sh" && chmod 755 "/jffs/addons/unbound/unbound_DNS_via_OVPN.sh" + fi + + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Unbound-over-VPN was enabled for VPNMON-R3" >> $logfile + echo -e "${CClear}" + unboundclient=$unboundovervpn + saveconfig + echo "Please reboot your router now if this is your first time or re-enabled Unbound over VPN" + read -rsp $'Press any key to continue...\n' -n1 key + break + + elif [ "$unboundovervpn" = "e" ]; then + echo -e "${CClear}\n[Exiting]"; sleep 2; break + fi + + else + echo -e "${CClear}\n[Exiting]"; sleep 2; break + fi + done + ;; + + 5) + if [ "$unboundclient" != "0" ]; then + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Show Expanded Unbound IP Information ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate below if you would like to show your full Unbound DNS Resolver IP on-${CClear}" + echo -e "${InvGreen} ${CClear} screen, or just an abbreviated color-coded indicator. Showing a full IP may in some${CClear}" + echo -e "${InvGreen} ${CClear} cases help with troubleshooting.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Use 0 to Disable, 1 to Enable. (Default = 0)" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo + echo -e "${CClear}Current: ${CGreen}$unboundshowipdisp${CClear}" ; echo + read -p "Please Choose? (Disable = 0, Enable = 1, e=Exit): " newunboundshowip + if [ "$newunboundshowip" = "0" ] + then + unboundshowip=0 + unboundshowipdisp="Disabled" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Expanded Unbound IP Information Disabled" >> $logfile + saveconfig + elif [ "$newunboundshowip" = "1" ] + then + unboundshowip=1 + unboundshowipdisp="Enabled" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Expanded Unbound IP Information Enabled" >> $logfile + saveconfig + elif [ "$newunboundshowip" = "e" ] + then + echo -e "\n[Exiting]"; sleep 2 + else + previousValue="$unboundshowip" + unboundshowip="${unboundshowip:=0}" + unboundshowipdisp="$([ "$unboundshowip" = "0" ] && echo "Disabled" || echo "Enabled")" + [ "$unboundshowip" != "$previousValue" ] && \ + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Expanded Unbound IP Information Disabled" >> $logfile + saveconfig + fi + fi + ;; + + 6) + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Refresh Custom Server Lists on -RESET Switch ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate below if you would like to automatically refresh your custom Server${CClear}" + echo -e "${InvGreen} ${CClear} Lists specified under the Edit/Run Server List Automation menu. This function will${CClear}" + echo -e "${InvGreen} ${CClear} run any defined CURL+JQ statements you have configured and save them back to your${CClear}" + echo -e "${InvGreen} ${CClear} Client Slot Server List files that will be used for VPN connections.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Use 0 to Disable, 1 to Enable. (Default = 0)" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo + echo -e "${CClear}Current: ${CGreen}$refreshserverlistsdisp${CClear}" ; echo + read -p "Please Choose? (Disable = 0, Enable = 1, e=Exit): " newRefreshServerList + if [ "$newRefreshServerList" = "0" ] + then + refreshserverlists=0 + refreshserverlistsdisp="Disabled" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom Server Lists Disabled on -RESET Switch" >> $logfile + saveconfig + elif [ "$newRefreshServerList" = "1" ] + then + refreshserverlists=1 + refreshserverlistsdisp="Enabled" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom Server Lists Enabled on -RESET Switch" >> $logfile + saveconfig + elif [ "$newRefreshServerList" = "e" ] + then + echo -e "\n[Exiting]"; sleep 2 + else + previousValue="$refreshserverlists" + refreshserverlists="${refreshserverlists:=0}" + refreshserverlistsdisp="$([ "$refreshserverlists" = "0" ] && echo "Disabled" || echo "Enabled")" + [ "$refreshserverlists" != "$previousValue" ] && \ + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom Server Lists $refreshserverlistsdisp on -RESET Switch" >> $logfile + saveconfig + fi + ;; + + 7) + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Provide WAN/Dual WAN Monitoring Functionality ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate below if you would like to monitor your WAN/Dual WAN connection for${CClear}" + echo -e "${InvGreen} ${CClear} connectivity issues that may impact your VPN connection. This functionality will${CClear}" + echo -e "${InvGreen} ${CClear} determine if your WAN connection is viable, and upon determining that the WAN is${CClear}" + echo -e "${InvGreen} ${CClear} down, VPNMON-R3 will kill all VPN connections and continue testing for a valid WAN${CClear}" + echo -e "${InvGreen} ${CClear} connection until it can restore your VPN connections.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Use 0 to Disable, 1 to Enable. (Default = 0)" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo + echo -e "${CClear}Current: ${CGreen}$monitorwandisp${CClear}" ; echo + read -p "Please Choose? (Disable = 0, Enable = 1, e=Exit): " newMonitorWAN + if [ "$newMonitorWAN" = "0" ] + then + monitorwan=0 + monitorwandisp="Disabled" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WAN Monitoring Disabled" >> $logfile + saveconfig + elif [ "$newMonitorWAN" = "1" ] + then + monitorwan=1 + monitorwandisp="Enabled" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WAN Monitoring Enabled" >> $logfile + saveconfig + elif [ "$newMonitorWAN" = "e" ] + then + echo -e "\n[Exiting]"; sleep 2 + else + previousValue="$monitorwan" + monitorwan="${monitorwan:=0}" + monitorwandisp="$([ "$monitorwan" = "0" ] && echo "Disabled" || echo "Enabled")" + [ "$monitorwan" != "$previousValue" ] && \ + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WAN Monitoring $monitorwandisp" >> $logfile + saveconfig + fi + ;; + + 8) + while true + do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Enable/Disable VPN/WG Slot Monitoring and Display ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate whether you want to enable or disable VPN/WG Slots from being${CClear}" + echo -e "${InvGreen} ${CClear} monitored and shown on the main VPNMON-R3 UI. This setting is meant for those${CClear}" + echo -e "${InvGreen} ${CClear} who are only running VPN or only WG on their router, and do not want to display${CClear}" + echo -e "${InvGreen} ${CClear} one or the other to only show relevant info.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Use the corresponding ${CGreen}()${CClear} key to enable/disable monitoring for each.${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + + if [ "$useovpn" = "1" ]; then useovpnDisp="${CGreen}Y${CCyan}"; else useovpn=0; useovpnDisp="${CRed}N${CCyan}"; fi + if [ "$usewg" = "1" ]; then usewgDisp="${CGreen}Y${CCyan}"; else usewg=0; usewgDisp="${CRed}N${CCyan}"; fi + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} OpenVPN${CClear} ${CGreen}(1) -${CClear} $useovpnDisp${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}Wireguard${CClear} ${CGreen}(2) -${CClear} $usewgDisp${CClear}" + echo "" + read -p "Please select? (1-2, e=Exit): " SelectSlot + case $SelectSlot in + 1) + if [ "$unboundclient" = "0" ]; then + if [ "$useovpn" = "0" ]; then + useovpn=1 + useovpnDisp="${CGreen}Y${CCyan}" + elif [ "$useovpn" = "1" ]; then + useovpn=0; useovpnDisp="${CRed}N${CCyan}" + fi + else + echo -e "${CClear}\n[Unable to disable VPN. Unbound Active on Slot VPN$unboundclient]"; sleep 3 + fi;; + 2) + if [ "$usewg" = "0" ]; then + usewg=1 + usewgDisp="${CGreen}Y${CCyan}" + elif [ "$usewg" = "1" ]; then + usewg=0 + usewgDisp="${CRed}N${CCyan}" + fi;; + [Ee]) + saveconfig + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN/WG Client Slot Monitoring/Display configuration saved" >> $logfile + timer="$timerloop" + break;; + esac + done + + ;; + + 9) + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Whitelist VPN Server IP Lists in Skynet ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate below if you would like to whitelist your VPN Server IP lists in${CClear}" + echo -e "${InvGreen} ${CClear} the Skynet Firewall. This provides for better stability in connecting to your VPN${CClear}" + echo -e "${InvGreen} ${CClear} provider, as there have been occasions where the Skynet blacklist will prevent${CClear}" + echo -e "${InvGreen} ${CClear} connections to your VPN Servers. This could cause a disruption in being able to${CClear}" + echo -e "${InvGreen} ${CClear} maintain a stable VPN connection. Note: Skynet must already be installed and${CClear}" + echo -e "${InvGreen} ${CClear} operational for this functionality to work.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Use 0 to Disable, 1 to Enable. (Default = 0)" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo + echo -e "${CClear}Current: ${CGreen}$updateskynetdisp${CClear}" ; echo + read -p "Please Choose? (Disable = 0, Enable = 1, e=Exit): " newUpdateSkyNet + if [ "$newUpdateSkyNet" = "1" ] + then + updateskynet=1 + updateskynetdisp="Enabled" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN Server IP Skynet Whitelisting Enabled" >> $logfile + saveconfig + elif [ "$newUpdateSkyNet" = "0" ] + then + updateskynet=0 + updateskynetdisp="Disabled" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN Server IP Skynet Whitelisting Disabled" >> $logfile + saveconfig + elif [ "$newUpdateSkyNet" = "e" ] + then + echo -e "\n[Exiting]"; sleep 2 + else + previousValue="$updateskynet" + updateskynet="${updateskynet:=0}" + updateskynetdisp="$([ "$updateskynet" = "0" ] && echo "Disabled" || echo "Enabled")" + [ "$updateskynet" != "$previousValue" ] && \ + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN Server IP Skynet Whitelisting $updateskynetdisp" >> $logfile + saveconfig + fi + ;; + + [Ee]) echo -e "${CClear}\n[Exiting]"; sleep 2; break ;; + + 10) + amtmevents + source "$config" + ;; + + 11) + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Reset spdMerlin Interfaces on VPN Reset ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate below if you would like to reset your spdMerlin Interfaces each${CClear}" + echo -e "${InvGreen} ${CClear} time VPNMON-R3 resets. This functionality will allow VPNMON-R3 to update spdMerlin${CClear}" + echo -e "${InvGreen} ${CClear} whenever a VPN reset occurs. SpdMerlin will then know which interfaces are active${CClear}" + echo -e "${InvGreen} ${CClear} in order to run manual or automated speedtests from.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Use 0 to Disable, 1 to Enable. (Default = 0)" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo + echo -e "${CClear}Current: ${CGreen}$rstspdmerlindisp${CClear}" ; echo + read -p "Please Choose? (Disable = 0, Enable = 1, e=Exit): " newrstspdmerlin + if [ "$newrstspdmerlin" = "0" ] + then + rstspdmerlin=0 + rstspdmerlindisp="Disabled" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: spdMerlin Interface Reset Disabled" >> $logfile + saveconfig + elif [ "$newrstspdmerlin" = "1" ] + then + rstspdmerlin=1 + rstspdmerlindisp="Enabled" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: spdMerlin Interface Reset Enabled" >> $logfile + saveconfig + elif [ "$newrstspdmerlin" = "e" ] + then + echo -e "\n[Exiting]"; sleep 2 + else + previousValue="$rstspdmerlin" + rstspdmerlin="${rstspdmerlin:=0}" + rstspdmerlindisp="$([ "$rstspdmerlin" = "0" ] && echo "Disabled" || echo "Enabled")" + [ "$rstspdmerlin" != "$previousValue" ] && \ + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: spdMerlin Interface Reset $monitorwandisp" >> $logfile + saveconfig + fi + ;; + + esac +done +} + +# ------------------------------------------------------------------------------------------------------------------------- +# vupdate is a function that provides a UI to check for script updates and allows you to install the latest version... + +vupdate() +{ + +updatecheck # Check for the latest version from source repository +while true; do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} VPNMON-R3 Update Utility ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} This utility allows you to check, download and install updates" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo "" + echo -e "Current Version: ${CGreen}$version${CClear}" + echo -e "Updated Version: ${CGreen}$DLversion${CClear}" + echo "" + if [ "$version" = "$DLversion" ] + then + echo -e "You are on the latest version! Would you like to download anyways? This will overwrite${CClear}" + echo -e "your local copy with the current build.${CClear}" + if promptyn "[y/n]: "; then + echo "" + echo -e "\nDownloading VPNMON-R3 ${CGreen}v$DLversion${CClear}" + curl --silent --retry 3 --connect-timeout 3 --max-time 5 --retry-delay 2 --retry-all-errors --fail "https://raw.githubusercontent.com/ViktorJp/VPNMON-R3/main/vpnmon-r3.sh" -o "/jffs/scripts/vpnmon-r3.sh" && chmod 755 "/jffs/scripts/vpnmon-r3.sh" + echo "" + echo -e "Download successful!${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Successfully downloaded and installed VPNMON-R3 v$DLversion" >> $logfile + echo "" + read -rsp $'Press any key to restart VPNMON-R3...\n' -n1 key + exec /jffs/scripts/vpnmon-r3.sh -setup + else + echo "" + echo "" + echo -e "Exiting Update Utility...${CClear}" + sleep 1 + return + fi + else + echo -e "Score! There is a new version out there! Would you like to update?${CClear}" + if promptyn "[y/n]: "; then + echo "" + echo -e "\nDownloading VPNMON-R3 ${CGreen}v$DLversion${CClear}" + curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 2 --retry-all-errors --fail "https://raw.githubusercontent.com/ViktorJp/VPNMON-R3/main/vpnmon-r3.sh" -o "/jffs/scripts/vpnmon-r3.sh" && chmod 755 "/jffs/scripts/vpnmon-r3.sh" + echo "" + echo -e "Download successful!${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Successfully downloaded and installed VPNMON-R3 v$DLversion" >> $logfile + echo "" + read -rsp $'Press any key to restart VPNMON-R3...\n' -n1 key + exec /jffs/scripts/vpnmon-r3.sh -setup + else + echo "" + echo "" + echo -e "Exiting Update Utility...${CClear}" + sleep 1 + return + fi + fi +done + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# updatecheck is a function that downloads the latest update version file, and compares it with what's currently installed + +updatecheck() +{ + + # Download the latest version file from the source repository + curl --silent --retry 3 --connect-timeout 3 --max-time 6 --retry-delay 2 --retry-all-errors --fail "https://raw.githubusercontent.com/ViktorJp/VPNMON-R3/main/version.txt" -o "/jffs/addons/vpnmon-r3.d/version.txt" + + if [ -f $dlverpath ] + then + # Read in its contents for the current version file + DLversion=$(cat $dlverpath) + + # Compare the new version with the old version and log it + if [ "$beta" = "1" ]; then # Check if Dev/Beta Mode is enabled and disable notification message + UpdateNotify=0 + elif [ "$DLversion" != "$version" ]; then + DLversionPF=$(printf "%-8s" $DLversion) + versionPF=$(printf "%-8s" $version) + UpdateNotify="${InvYellow} ${InvDkGray}${CWhite} Update available: v$versionPF -> v$DLversionPF ${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: A new update (v$DLversion) is available to download" >> $logfile + else + UpdateNotify=0 + fi + fi +} + +# ------------------------------------------------------------------------------------------------------------------------- +# vuninstall is a function that uninstalls and removes all traces of vpnmon-r3 from your router... + +vuninstall() +{ + +while true; do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Uninstall Utility ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} You are about to uninstall VPNMON-R3! This action is irreversible." + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo "" + echo -e "Do you wish to proceed?${CClear}" + if promptyn "(y/n): "; then + echo "" + echo -e "\nAre you sure? Please type 'y' to validate you wish to proceed.${CClear}" + if promptyn "(y/n): "; then + clear + #Remove and uninstall files/directories + rm -f -r /jffs/addons/vpnmon-r3.d + rm -f /jffs/scripts/vpnmon-r3.sh + sed -i -e '/vpnmon-r3.sh/d' /jffs/scripts/services-start + cru d RunVPNMONR3reset + echo "" + echo -e "\nVPNMON-R3 has been uninstalled...${CClear}" + echo "" + exit 0 + else + echo "" + echo -e "\nExiting Uninstall Utility...${CClear}" + sleep 1 + return + fi + else + echo "" + echo -e "\nExiting Uninstall Utility...${CClear}" + sleep 1 + return + fi +done +} + +# ------------------------------------------------------------------------------------------------------------------------- +# vlogs is a function that calls the nano text editor to view the BACKUPMON log file + +vlogs() +{ + +export TERM=linux +nano +999999 --linenumbers $logfile +timer="$timerloop" + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# vpnslots lets you pick which vpn slot numbers are being monitored by vpnmon-r3 + +vpnslots() +{ + +while true +do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} VPN/WG Client Slot Monitoring ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate which VPN/WG slots you would like VPNMON-R3 to monitor.${CClear}" + echo -e "${InvGreen} ${CClear} Use the corresponding ${CGreen}()${CClear} key to enable/disable monitoring for each slot:${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + + if [ "$availableslots" = "1 2" ] + then + if [ "$VPN1" = "1" ]; then VPN1Disp="${CGreen}Y${CCyan}"; else VPN1=0; VPN1Disp="${CRed}N${CCyan}"; fi + if [ "$VPN2" = "1" ]; then VPN2Disp="${CGreen}Y${CCyan}"; else VPN2=0; VPN2Disp="${CRed}N${CCyan}"; fi + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN1${CClear} ${CGreen}(1) -${CClear} $VPN1Disp${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN2${CClear} ${CGreen}(2) -${CClear} $VPN2Disp${CClear}" + echo "" + read -p "Please select? (1-2, e=Exit): " SelectSlot + case $SelectSlot in + 1) if [ "$VPN1" = "0" ]; then VPN1=1; VPN1Disp="${CGreen}Y${CCyan}"; elif [ "$VPN1" = "1" ]; then VPN1=0; VPN1Disp="${CRed}N${CCyan}"; fi;; + 2) if [ "$VPN2" = "0" ]; then VPN2=1; VPN2Disp="${CGreen}Y${CCyan}"; elif [ "$VPN2" = "1" ]; then VPN2=0; VPN2Disp="${CRed}N${CCyan}"; fi;; + [Ee]) + { echo 'VPN1='$VPN1 + echo 'VPN2='$VPN2 + } > /jffs/addons/vpnmon-r3.d/vr3clients.txt + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN Client Slot Monitoring configuration saved" >> $logfile + timer="$timerloop" + break;; + esac + + elif [ "$availableslots" = "1 2 3 4 5" ] + then + if [ "$VPN1" = "1" ]; then VPN1Disp="${CGreen}Y${CCyan}"; else VPN1=0; VPN1Disp="${CRed}N${CCyan}"; fi + if [ "$VPN2" = "1" ]; then VPN2Disp="${CGreen}Y${CCyan}"; else VPN2=0; VPN2Disp="${CRed}N${CCyan}"; fi + if [ "$VPN3" = "1" ]; then VPN3Disp="${CGreen}Y${CCyan}"; else VPN3=0; VPN3Disp="${CRed}N${CCyan}"; fi + if [ "$VPN4" = "1" ]; then VPN4Disp="${CGreen}Y${CCyan}"; else VPN4=0; VPN4Disp="${CRed}N${CCyan}"; fi + if [ "$VPN5" = "1" ]; then VPN5Disp="${CGreen}Y${CCyan}"; else VPN5=0; VPN5Disp="${CRed}N${CCyan}"; fi + if [ "$WG1" = "1" ]; then WG1Disp="${CGreen}Y${CCyan}"; else WG1=0; WG1Disp="${CRed}N${CCyan}"; fi + if [ "$WG2" = "1" ]; then WG2Disp="${CGreen}Y${CCyan}"; else WG2=0; WG2Disp="${CRed}N${CCyan}"; fi + if [ "$WG3" = "1" ]; then WG3Disp="${CGreen}Y${CCyan}"; else WG3=0; WG3Disp="${CRed}N${CCyan}"; fi + if [ "$WG4" = "1" ]; then WG4Disp="${CGreen}Y${CCyan}"; else WG4=0; WG4Disp="${CRed}N${CCyan}"; fi + if [ "$WG5" = "1" ]; then WG5Disp="${CGreen}Y${CCyan}"; else WG5=0; WG5Disp="${CRed}N${CCyan}"; fi + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN1${CClear} ${CGreen}(1) -${CClear} $VPN1Disp${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN2${CClear} ${CGreen}(2) -${CClear} $VPN2Disp${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN3${CClear} ${CGreen}(3) -${CClear} $VPN3Disp${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN4${CClear} ${CGreen}(4) -${CClear} $VPN4Disp${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN5${CClear} ${CGreen}(5) -${CClear} $VPN5Disp${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} WG1${CClear} ${CGreen}(6) -${CClear} $WG1Disp${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} WG2${CClear} ${CGreen}(7) -${CClear} $WG2Disp${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} WG3${CClear} ${CGreen}(8) -${CClear} $WG3Disp${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} WG4${CClear} ${CGreen}(9) -${CClear} $WG4Disp${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} WG5${CClear} ${CGreen}(0) -${CClear} $WG5Disp${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo "" + read -p "Please select? (1-0, e=Exit): " SelectSlot + case $SelectSlot in + 1) if [ "$VPN1" = "0" ]; then VPN1=1; VPN1Disp="${CGreen}Y${CCyan}"; elif [ "$VPN1" = "1" ]; then VPN1=0; VPN1Disp="${CRed}N${CCyan}"; fi;; + 2) if [ "$VPN2" = "0" ]; then VPN2=1; VPN2Disp="${CGreen}Y${CCyan}"; elif [ "$VPN2" = "1" ]; then VPN2=0; VPN2Disp="${CRed}N${CCyan}"; fi;; + 3) if [ "$VPN3" = "0" ]; then VPN3=1; VPN3Disp="${CGreen}Y${CCyan}"; elif [ "$VPN3" = "1" ]; then VPN3=0; VPN3Disp="${CRed}N${CCyan}"; fi;; + 4) if [ "$VPN4" = "0" ]; then VPN4=1; VPN4Disp="${CGreen}Y${CCyan}"; elif [ "$VPN4" = "1" ]; then VPN4=0; VPN4Disp="${CRed}N${CCyan}"; fi;; + 5) if [ "$VPN5" = "0" ]; then VPN5=1; VPN5Disp="${CGreen}Y${CCyan}"; elif [ "$VPN5" = "1" ]; then VPN5=0; VPN5Disp="${CRed}N${CCyan}"; fi;; + 6) if [ "$WG1" = "0" ]; then WG1=1; WG1Disp="${CGreen}Y${CCyan}"; elif [ "$WG1" = "1" ]; then WG1=0; WG1Disp="${CRed}N${CCyan}"; fi;; + 7) if [ "$WG2" = "0" ]; then WG2=1; WG2Disp="${CGreen}Y${CCyan}"; elif [ "$WG2" = "1" ]; then WG2=0; WG2Disp="${CRed}N${CCyan}"; fi;; + 8) if [ "$WG3" = "0" ]; then WG3=1; WG3Disp="${CGreen}Y${CCyan}"; elif [ "$WG3" = "1" ]; then WG3=0; WG3Disp="${CRed}N${CCyan}"; fi;; + 9) if [ "$WG4" = "0" ]; then WG4=1; WG4Disp="${CGreen}Y${CCyan}"; elif [ "$WG4" = "1" ]; then WG4=0; WG4Disp="${CRed}N${CCyan}"; fi;; + 0) if [ "$WG5" = "0" ]; then WG5=1; WG5Disp="${CGreen}Y${CCyan}"; elif [ "$WG5" = "1" ]; then WG5=0; WG5Disp="${CRed}N${CCyan}"; fi;; + [Ee]) + { echo 'VPN1='$VPN1 + echo 'VPN2='$VPN2 + echo 'VPN3='$VPN3 + echo 'VPN4='$VPN4 + echo 'VPN5='$VPN5 + echo 'WG1='$WG1 + echo 'WG2='$WG2 + echo 'WG3='$WG3 + echo 'WG4='$WG4 + echo 'WG5='$WG5 + } > /jffs/addons/vpnmon-r3.d/vr3clients.txt + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN/WG Client Slot Monitoring configuration saved" >> $logfile + timer="$timerloop" + break;; + esac + fi +done + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# amtmevents lets you pick success or failure amtm email notification selections + +amtmevents() +{ + +while true +do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} AMTM Email Notifications ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate if you would like VPNMON-R3 to send you email notifications for WAN${CClear}" + echo -e "${InvGreen} ${CClear} or VPN slot failures, or successes on scheduled VPN resets, or both? PLEASE NOTE:${CClear}" + echo -e "${InvGreen} ${CClear} This does require that AMTM email has been set up successfully under AMTM -> em${CClear}" + echo -e "${InvGreen} ${CClear} (email settings). Once you are able to send and receive test emails from AMTM, you${CClear}" + echo -e "${InvGreen} ${CClear} may use this functionality in VPNMON-R3. Additionally, this functionality will${CClear}" + echo -e "${InvGreen} ${CClear} download an AMTM email interface library courtesy of @Martinsky, and will be${CClear}" + echo -e "${InvGreen} ${CClear} located under a new common shared library folder called: /jffs/addons/shared-libs.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Secondarily, you can choose to rate limit the rate at which emails are sent to${CClear}" + echo -e "${InvGreen} ${CClear} your email account per hour. (0=Disabled, 1-9999)${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Use the corresponding ${CGreen}()${CClear} key to enable/disable email event notifications:${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + + if [ "$amtmemailsuccess" = "1" ]; then amtmemailsuccessdisp="${CGreen}Y${CCyan}"; else amtmemailsuccess=0; amtmemailsuccessdisp="${CRed}N${CCyan}"; fi + if [ "$amtmemailfailure" = "1" ]; then amtmemailfailuredisp="${CGreen}Y${CCyan}"; else amtmemailfailure=0; amtmemailfailuredisp="${CRed}N${CCyan}"; fi + if [ "$ratelimit" = "0" ]; then ratelimitdisp="Disabled"; else ratelimitdisp=$ratelimit; fi + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN Success Event Notifications${CClear} ${CGreen}(1) -${CClear} $amtmemailsuccessdisp${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN Failure Event Notifications${CClear} ${CGreen}(2) -${CClear} $amtmemailfailuredisp${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}Set Email Rate Limit (per hour)${CClear} ${CGreen}(r) - $ratelimitdisp${CClear}" + echo "" + read -p "Please select? (1-2, r=Set Email Rate Limit, t=Test Email, e=Exit): " SelectSlot + case $SelectSlot in + 1) if [ "$amtmemailsuccess" = "0" ]; then amtmemailsuccess=1; amtmemailsuccessdisp="${CGreen}Y${CCyan}"; elif [ "$amtmemailsuccess" = "1" ]; then amtmemailsuccess=0; amtmemailsuccessdisp="${CRed}N${CCyan}"; fi;; + 2) if [ "$amtmemailfailure" = "0" ]; then amtmemailfailure=1; amtmemailfailuredisp="${CGreen}Y${CCyan}"; elif [ "$amtmemailfailure" = "1" ]; then amtmemailfailure=0; amtmemailfailuredisp="${CRed}N${CCyan}"; fi;; + [Tt]) + if [ -f "$CUSTOM_EMAIL_LIBFile" ] + then + . "$CUSTOM_EMAIL_LIBFile" + + if [ -z "${CEM_LIB_VERSION:+xSETx}" ] || \ + _CheckLibraryUpdates_CEM_ "$CUSTOM_EMAIL_LIBDir" quiet + then + _DownloadCEMLibraryFile_ "update" + fi + else + _DownloadCEMLibraryFile_ "install" + fi + + cemIsFormatHTML=true + cemIsVerboseMode=true ## true OR false ## + emailBodyTitle="Testing Email Notification" + emailSubject="TEST: VPNMON-R3 Email Notification" + tmpEMailBodyFile="/tmp/var/tmp/tmpEMailBody_${scriptFileNTag}.$$.TXT" + + { + printf "This is a TEST to check & verify if sending email notifications is working well from VPNMON-R3.\n" + } > "$tmpEMailBodyFile" + + _SendEMailNotification_ "VPNMON-R3 v$version" "$emailSubject" "$tmpEMailBodyFile" "$emailBodyTitle" + + echo ; echo + read -rsp $'Press any key to acknowledge...\n' -n1 key + ;; + + [Rr]) + echo "" + read -p "Please enter new Email Rate Limit (per hour)? (0=disabled, 1-9999, e=Exit): " newratelimit + if [ "$newratelimit" = "e" ] + then + echo -e "\n[Exiting]"; sleep 2 + elif echo "$newratelimit" | grep -qE "^(0|[1-9][0-9]{0,3})$" && \ + [ "$newratelimit" -ge 0 ] && [ "$newratelimit" -le 9999 ] + then + ratelimit="$newratelimit" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Email Rate Limit entered (per hour): $ratelimit" >> $logfile + saveconfig + else + previousValue="$ratelimit" + ratelimit="${ratelimit:=0}" + [ "$ratelimit" != "$previousValue" ] && \ + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Email Rate Limit entered (per hour): $ratelimit" >> $logfile + saveconfig + fi + ;; + + [Ee]) + saveconfig + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: AMTM Email notification configuration saved" >> $logfile + timer="$timerloop" + break;; + esac +done + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# vpnserverlistmaint lets you pick which vpn slot server list you want to edit/maintain + +vpnserverlistmaint() +{ + +while true +do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} VPN Client Slot Server List Maintenance ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate which VPN Slot Server List you would like to edit/maintain.${CClear}" + echo -e "${InvGreen} ${CClear} Use the corresponding ${CGreen}()${CClear} key to launch and edit the list with the NANO text editor${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} VPN INSTRUCTIONS: Enter a single column of IP addresses or hostnames, as required by${CClear}" + echo -e "${InvGreen} ${CClear} your VPN provider. ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} NANO INSTRUCTIONS: CTRL-O + Enter (save), CTRL-X (exit)${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN1${CClear} ${CGreen}(1)${CClear}" + if [ -f /jffs/addons/vpnmon-r3.d/vr3svr1.txt ]; then + iplist=$(awk -vORS=, '{ print $1 }' /jffs/addons/vpnmon-r3.d/vr3svr1.txt | sed 's/,$/\n/') + echo -en "${InvGreen} ${CClear} Contents: "; printf "%.75s>\n" $iplist + else + echo -e "${InvGreen} ${CClear} Contents: " + fi + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN2${CClear} ${CGreen}(2)${CClear}" + if [ -f /jffs/addons/vpnmon-r3.d/vr3svr2.txt ]; then + iplist=$(awk -vORS=, '{ print $1 }' /jffs/addons/vpnmon-r3.d/vr3svr2.txt | sed 's/,$/\n/') + echo -en "${InvGreen} ${CClear} Contents: "; printf "%.75s>\n" $iplist + else + echo -e "${InvGreen} ${CClear} Contents: " + fi + echo -e "${InvGreen} ${CClear}" + + if [ "$availableslots" = "1 2" ] + then + echo "" + read -p "Please select? (1-2, e=Exit): " SelectSlot + case $SelectSlot in + 1) export TERM=linux; nano +999999 --linenumbers /jffs/addons/vpnmon-r3.d/vr3svr1.txt;; + 2) export TERM=linux; nano +999999 --linenumbers /jffs/addons/vpnmon-r3.d/vr3svr2.txt;; + [Ee]) + timer="$timerloop" + break;; + esac + fi + + if [ "$availableslots" = "1 2 3 4 5" ] + then + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN3${CClear} ${CGreen}(3)${CClear}" + if [ -f /jffs/addons/vpnmon-r3.d/vr3svr3.txt ]; then + iplist=$(awk -vORS=, '{ print $1 }' /jffs/addons/vpnmon-r3.d/vr3svr3.txt | sed 's/,$/\n/') + echo -en "${InvGreen} ${CClear} Contents: "; printf "%.75s>\n" $iplist + else + echo -e "${InvGreen} ${CClear} Contents: " + fi + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN4${CClear} ${CGreen}(4)${CClear}" + if [ -f /jffs/addons/vpnmon-r3.d/vr3svr4.txt ]; then + iplist=$(awk -vORS=, '{ print $1 }' /jffs/addons/vpnmon-r3.d/vr3svr4.txt | sed 's/,$/\n/') + echo -en "${InvGreen} ${CClear} Contents: "; printf "%.75s>\n" $iplist + else + echo -e "${InvGreen} ${CClear} Contents: " + fi + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN5${CClear} ${CGreen}(5)${CClear}" + if [ -f /jffs/addons/vpnmon-r3.d/vr3svr5.txt ]; then + iplist=$(awk -vORS=, '{ print $1 }' /jffs/addons/vpnmon-r3.d/vr3svr5.txt | sed 's/,$/\n/') + echo -en "${InvGreen} ${CClear} Contents: "; printf "%.75s>\n" $iplist + else + echo -e "${InvGreen} ${CClear} Contents: " + fi + echo "" + read -p "Please select? (1-5, e=Exit): " SelectSlot + case $SelectSlot in + 1) export TERM=linux; nano +999999 --linenumbers /jffs/addons/vpnmon-r3.d/vr3svr1.txt;; + 2) export TERM=linux; nano +999999 --linenumbers /jffs/addons/vpnmon-r3.d/vr3svr2.txt;; + 3) export TERM=linux; nano +999999 --linenumbers /jffs/addons/vpnmon-r3.d/vr3svr3.txt;; + 4) export TERM=linux; nano +999999 --linenumbers /jffs/addons/vpnmon-r3.d/vr3svr4.txt;; + 5) export TERM=linux; nano +999999 --linenumbers /jffs/addons/vpnmon-r3.d/vr3svr5.txt;; + [Ee]) + timer="$timerloop" + break;; + esac + fi + +done + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# wgserverlistmaint lets you pick which wg slot server list you want to edit/maintain + +wgserverlistmaint() +{ + +if [ "$availableslots" = "1 2" ] + then + return +fi + +while true +do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} WG Client Slot Server List Maintenance ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate which WG Slot Server List you would like to edit/maintain.${CClear}" + echo -e "${InvGreen} ${CClear} Use the corresponding ${CGreen}()${CClear} key to launch and edit the list with the NANO text editor${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} WG INSTRUCTIONS: Enter a 5-field comma-delimited row of WG Connections${CClear}" + echo -e "${InvGreen} ${CClear} Format: ConnectionName,EndpointIP,EndpointPort,PrivateKey,PublicKey${CClear}" + echo -e "${InvGreen} ${CClear} Example: ${CGreen}City WG,143.32.55.23,34334,fasdkkfj44j38affkasdjfj=,221t949asas42dfj32323kf=${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} NANO INSTRUCTIONS: CTRL-O + Enter (save), CTRL-X (exit)${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}WG1${CClear} ${CGreen}(6)${CClear}" + if [ -f /jffs/addons/vpnmon-r3.d/vr3wgsvr1.txt ]; then + wglist=$(awk -F, '{printf "%s%s", sep, $1; sep=","} END {print ""}' /jffs/addons/vpnmon-r3.d/vr3wgsvr1.txt | awk '{print substr($0, 1, 75) ">"}') + echo -en "${InvGreen} ${CClear} Contents: "; echo $wglist + else + echo -e "${InvGreen} ${CClear} Contents: " + fi + echo -e "${InvGreen} ${CClear}" + + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}WG2${CClear} ${CGreen}(7)${CClear}" + if [ -f /jffs/addons/vpnmon-r3.d/vr3wgsvr2.txt ]; then + wglist=$(awk -F, '{printf "%s%s", sep, $1; sep=","} END {print ""}' /jffs/addons/vpnmon-r3.d/vr3wgsvr2.txt | awk '{print substr($0, 1, 75) ">"}') + echo -en "${InvGreen} ${CClear} Contents: "; echo $wglist + else + echo -e "${InvGreen} ${CClear} Contents: " + fi + echo -e "${InvGreen} ${CClear}" + + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}WG3${CClear} ${CGreen}(8)${CClear}" + if [ -f /jffs/addons/vpnmon-r3.d/vr3wgsvr3.txt ]; then + wglist=$(awk -F, '{printf "%s%s", sep, $1; sep=","} END {print ""}' /jffs/addons/vpnmon-r3.d/vr3wgsvr3.txt | awk '{print substr($0, 1, 75) ">"}') + echo -en "${InvGreen} ${CClear} Contents: "; echo $wglist + else + echo -e "${InvGreen} ${CClear} Contents: " + fi + echo -e "${InvGreen} ${CClear}" + + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}WG4${CClear} ${CGreen}(9)${CClear}" + if [ -f /jffs/addons/vpnmon-r3.d/vr3wgsvr4.txt ]; then + wglist=$(awk -F, '{printf "%s%s", sep, $1; sep=","} END {print ""}' /jffs/addons/vpnmon-r3.d/vr3wgsvr4.txt | awk '{print substr($0, 1, 75) ">"}') + echo -en "${InvGreen} ${CClear} Contents: "; echo $wglist + else + echo -e "${InvGreen} ${CClear} Contents: " + fi + echo -e "${InvGreen} ${CClear}" + + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}WG5${CClear} ${CGreen}(0)${CClear}" + if [ -f /jffs/addons/vpnmon-r3.d/vr3wgsvr5.txt ]; then + wglist=$(awk -F, '{printf "%s%s", sep, $1; sep=","} END {print ""}' /jffs/addons/vpnmon-r3.d/vr3wgsvr5.txt | awk '{print substr($0, 1, 75) ">"}') + echo -en "${InvGreen} ${CClear} Contents: "; echo $wglist + else + echo -e "${InvGreen} ${CClear} Contents: " + fi + echo "" + read -p "Please select? (6-0, e=Exit): " SelectSlot + case $SelectSlot in + 6) export TERM=linux; nano +999999 --linenumbers /jffs/addons/vpnmon-r3.d/vr3wgsvr1.txt;; + 7) export TERM=linux; nano +999999 --linenumbers /jffs/addons/vpnmon-r3.d/vr3wgsvr2.txt;; + 8) export TERM=linux; nano +999999 --linenumbers /jffs/addons/vpnmon-r3.d/vr3wgsvr3.txt;; + 9) export TERM=linux; nano +999999 --linenumbers /jffs/addons/vpnmon-r3.d/vr3wgsvr4.txt;; + 0) export TERM=linux; nano +999999 --linenumbers /jffs/addons/vpnmon-r3.d/vr3wgsvr5.txt;; + [Ee]) + timer="$timerloop" + break;; + esac + +done + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# vpnserverlistautomation lets you pick which vpn slot server list automation you want to edit/execute + +vpnserverlistautomation() +{ + +while true +do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} WG/VPN Client Slot Server List Automation ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate which VPN Slot Automation Script you would like to edit/execute.${CClear}" + echo -e "${InvGreen} ${CClear} Use the corresponding ${CGreen}()${CClear} key to edit or launch your update statements${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} VPN INSTRUCTIONS: Insert CURL statement that outputs a single-column Server IP list${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} WG INSTRUCTIONS: Insert CURL statement that outputs a 5-field comma-separated list${CClear}" + echo -e "${InvGreen} ${CClear} in this format: ConnectionName,EndpointIP,EndpointPort,PrivateKey,PublicKey${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN1${CClear} ${CGreen}(e1)${CClear} View/Edit | ${CGreen}(x1)${CClear} Execute | ${CGreen}(s1)${CClear} Skynet WL Import${CClear}" + if [ -z "$automation1" ] || [ "$automation1" = "" ]; then + echo -e "${InvGreen} ${CClear} Contents: " + else + automation1unenc=$(echo "$automation1" | openssl enc -d -base64 -A) + echo -en "${InvGreen} ${CClear} Contents: ${InvDkGray}${CWhite}"; printf "%.75s>\n" "$automation1unenc" + fi + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN2${CClear} ${CGreen}(e2)${CClear} View/Edit | ${CGreen}(x2)${CClear} Execute | ${CGreen}(s2)${CClear} Skynet WL Import${CClear}" + if [ -z "$automation2" ] || [ "$automation2" = "" ]; then + echo -e "${InvGreen} ${CClear} Contents: " + else + automation2unenc=$(echo "$automation2" | openssl enc -d -base64 -A) + echo -en "${InvGreen} ${CClear} Contents: ${InvDkGray}${CWhite}"; printf "%.75s>\n" "$automation2unenc" + fi + echo -e "${InvGreen} ${CClear}" + + if [ "$availableslots" = "1 2" ]; then + echo "" + read -p "Please select? (e1-e2, x1-x2, s1-s2, e=Exit): " SelectSlot2 + case $SelectSlot2 in + e1) + echo "" + if [ "$automation1" = "" ] || [ -z "$automation1" ]; then + echo -e "${CClear}Old Script: " + echo "" + else + echo -en "${CClear}Old Script: "; echo "$automation1" | openssl enc -d -base64 -A + echo "" + fi + read -rp 'Enter New Script (e=Exit): ' automation1new + if [ "$automation1new" = "" ] || [ -z "$automation1new" ]; then + automation1="" + saveconfig + elif [ "$automation1new" = "e" ]; then + echo ""; echo -e "${CGreen}[Exiting]${CClear}"; sleep 1 + else + automation1=`echo $automation1new | openssl enc -base64 -A` + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Custom VPN Server List Command entered for VPN Slot 1" >> $logfile + saveconfig + fi + ;; + + x1) + echo "" + echo -e "${CGreen}[Executing Script]${CClear}" + automation1unenc=$(echo "$automation1" | openssl enc -d -base64 -A) + echo -e "${CClear}Running: $automation1unenc" + echo "" + eval "$automation1unenc" > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + #Determine how many server entries are in each of the vpn slot alternate server files + if [ -f /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt ]; then + dlcnt=$(cat /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt | wc -l) >/dev/null 2>&1 + if [ $dlcnt -lt 1 ]; then + dlcnt=0 + elif [ -z $dlcnt ]; then + dlcnt=0 + fi + else + dlcnt=0 + fi + + if [ "$dlcnt" -gt 1 ] + then + cp "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" "/jffs/addons/vpnmon-r3.d/vr3svr1.txt" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source]${CClear}" + echo "" + echo -e "${CGreen}[Saved to VPN Client Slot 1 Server List]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom VPN Server List Command executed for VPN Slot 1" >> $logfile + echo "" + skynetwhitelist 1 + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + else + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source - Preserving Original Server List]${CClear}" + echo "" + echo -e "${CGreen}[Please check Query Language or VPN Service API may be down]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom VPN Client Server List Query for VPN Slot 1 yielded 0 rows -- Query may be invalid or VPN API service may be down" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + fi + ;; + + s1) + echo "" + echo -e "${CGreen}[Executing Skynet Whitelist Import]${CClear}" + firewall import whitelist /jffs/addons/vpnmon-r3.d/vr3svr1.txt "VPNMON-R3 VPN Slot 1 Import" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Contents of VPN Slot 1 Imported]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Skynet Whitelist imported for VPN Slot 1" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + ;; + + e2) + echo "" + if [ "$automation2" = "" ] || [ -z "$automation2" ]; then + echo -e "${CClear}Old Script: " + echo "" + else + echo -en "${CClear}Old Script: "; echo "$automation2" | openssl enc -d -base64 -A + echo "" + fi + read -rp 'Enter New Script (e=Exit): ' automation2new + if [ "$automation2new" = "" ] || [ -z "$automation2new" ]; then + automation2="" + saveconfig + elif [ "$automation2new" = "e" ]; then + echo ""; echo -e "${CGreen}[Exiting]${CClear}"; sleep 1 + else + automation2=`echo $automation2new | openssl enc -base64 -A` + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Custom VPN Server List Command entered for VPN Slot 2" >> $logfile + saveconfig + fi + ;; + + x2) + echo "" + echo -e "${CGreen}[Executing Script]${CClear}" + automation2unenc=$(echo "$automation2" | openssl enc -d -base64 -A) + echo -e "${CClear}Running: $automation2unenc" + echo "" + eval "$automation2unenc" > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + #Determine how many server entries are in each of the vpn slot alternate server files + if [ -f /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt ]; then + dlcnt=$(cat /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt | wc -l) >/dev/null 2>&1 + if [ $dlcnt -lt 1 ]; then + dlcnt=0 + elif [ -z $dlcnt ]; then + dlcnt=0 + fi + else + dlcnt=0 + fi + + if [ "$dlcnt" -gt 1 ] + then + cp "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" "/jffs/addons/vpnmon-r3.d/vr3svr2.txt" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source]${CClear}" + echo "" + echo -e "${CGreen}[Saved to VPN Client Slot 2 Server List]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom VPN Server List Command executed for VPN Slot 2" >> $logfile + echo "" + skynetwhitelist 2 + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + else + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source - Preserving Original Server List]${CClear}" + echo "" + echo -e "${CGreen}[Please check Query Language or VPN Service API may be down]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom VPN Client Server List Query for VPN Slot 2 yielded 0 rows -- Query may be invalid or VPN API service may be down" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + fi + ;; + + s2) + echo "" + echo -e "${CGreen}[Executing Skynet Whitelist Import]${CClear}" + firewall import whitelist /jffs/addons/vpnmon-r3.d/vr3svr2.txt "VPNMON-R3 VPN Slot 2 Import" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Contents of VPN Slot 2 Imported]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Skynet Whitelist imported for VPN Slot 2" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + ;; + + [Ee]) + timer="$timerloop" + break;; + esac + fi + + if [ "$availableslots" = "1 2 3 4 5" ]; then + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN3${CClear} ${CGreen}(e3)${CClear} View/Edit | ${CGreen}(x3)${CClear} Execute | ${CGreen}(s3)${CClear} Skynet WL Import${CClear}" + if [ -z "$automation3" ] || [ "$automation3" = "" ]; then + echo -e "${InvGreen} ${CClear} Contents: " + else + automation3unenc=$(echo "$automation3" | openssl enc -d -base64 -A) + echo -en "${InvGreen} ${CClear} Contents: ${InvDkGray}${CWhite}"; printf "%.75s>\n" "$automation3unenc" + fi + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN4${CClear} ${CGreen}(e4)${CClear} View/Edit | ${CGreen}(x4)${CClear} Execute | ${CGreen}(s4)${CClear} Skynet WL Import${CClear}" + if [ -z "$automation4" ] || [ "$automation4" = "" ]; then + echo -e "${InvGreen} ${CClear} Contents: " + else + automation4unenc=$(echo "$automation4" | openssl enc -d -base64 -A) + echo -en "${InvGreen} ${CClear} Contents: ${InvDkGray}${CWhite}"; printf "%.75s>\n" "$automation4unenc" + fi + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN5${CClear} ${CGreen}(e5)${CClear} View/Edit | ${CGreen}(x5)${CClear} Execute | ${CGreen}(s5)${CClear} Skynet WL Import${CClear}" + if [ -z "$automation5" ] || [ "$automation5" = "" ]; then + echo -e "${InvGreen} ${CClear} Contents: " + else + automation5unenc=$(echo "$automation5" | openssl enc -d -base64 -A) + echo -en "${InvGreen} ${CClear} Contents: ${InvDkGray}${CWhite}"; printf "%.75s>\n" "$automation5unenc" + fi + echo -e "${InvGreen} ${CClear}" + + ##-------------------------------------## + ## Added by Dan G. [2025-Jul-15] ## + ##-------------------------------------## + + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} WG1${CClear} ${CGreen}(e6)${CClear} View/Edit | ${CGreen}(x6)${CClear} Execute | ${CGreen}(s6)${CClear} Skynet WL Import${CClear}" + if [ -z "$wgautomation1" ] || [ "$wgautomation1" = "" ]; then + echo -e "${InvGreen} ${CClear} Contents: " + else + wgautomation1unenc=$(echo "$wgautomation1" | openssl enc -d -base64 -A) + echo -en "${InvGreen} ${CClear} Contents: ${InvDkGray}${CWhite}"; printf "%.75s>\n" "$wgautomation1unenc" + fi + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} WG2${CClear} ${CGreen}(e7)${CClear} View/Edit | ${CGreen}(x7)${CClear} Execute | ${CGreen}(s7)${CClear} Skynet WL Import${CClear}" + if [ -z "$wgautomation2" ] || [ "$wgautomation2" = "" ]; then + echo -e "${InvGreen} ${CClear} Contents: " + else + wgautomation2unenc=$(echo "$wgautomation2" | openssl enc -d -base64 -A) + echo -en "${InvGreen} ${CClear} Contents: ${InvDkGray}${CWhite}"; printf "%.75s>\n" "$wgautomation2unenc" + fi + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} WG3${CClear} ${CGreen}(e8)${CClear} View/Edit | ${CGreen}(x8)${CClear} Execute | ${CGreen}(s8)${CClear} Skynet WL Import${CClear}" + if [ -z "$wgautomation3" ] || [ "$wgautomation3" = "" ]; then + echo -e "${InvGreen} ${CClear} Contents: " + else + wgautomation3unenc=$(echo "$wgautomation3" | openssl enc -d -base64 -A) + echo -en "${InvGreen} ${CClear} Contents: ${InvDkGray}${CWhite}"; printf "%.75s>\n" "$wgautomation3unenc" + fi + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} WG4${CClear} ${CGreen}(e9)${CClear} View/Edit | ${CGreen}(x9)${CClear} Execute | ${CGreen}(s9)${CClear} Skynet WL Import${CClear}" + if [ -z "$wgautomation4" ] || [ "$wgautomation4" = "" ]; then + echo -e "${InvGreen} ${CClear} Contents: " + else + wgautomation4unenc=$(echo "$wgautomation4" | openssl enc -d -base64 -A) + echo -en "${InvGreen} ${CClear} Contents: ${InvDkGray}${CWhite}"; printf "%.75s>\n" "$wgautomation4unenc" + fi + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite} WG5${CClear} ${CGreen}(e0)${CClear} View/Edit | ${CGreen}(x0)${CClear} Execute | ${CGreen}(s0)${CClear} Skynet WL Import${CClear}" + if [ -z "$wgautomation5" ] || [ "$wgautomation5" = "" ]; then + echo -e "${InvGreen} ${CClear} Contents: " + else + wgautomation5unenc=$(echo "$wgautomation5" | openssl enc -d -base64 -A) + echo -en "${InvGreen} ${CClear} Contents: ${InvDkGray}${CWhite}"; printf "%.75s>\n" "$wgautomation5unenc" + fi + echo "" + read -p "Please select? (e1-e0, x1-x0, s1-s0, e=Exit): " SelectSlot5 + case $SelectSlot5 in + e1) + echo "" + if [ "$automation1" = "" ] || [ -z "$automation1" ]; then + echo -e "${CClear}Old Script: " + echo "" + else + echo -en "${CClear}Old Script: "; echo "$automation1" | openssl enc -d -base64 -A + echo "" + fi + read -rp 'Enter New Script (e=Exit): ' automation1new + if [ "$automation1new" = "" ] || [ -z "$automation1new" ]; then + automation1="" + saveconfig + elif [ "$automation1new" = "e" ]; then + echo ""; echo -e "${CGreen}[Exiting]${CClear}"; sleep 1 + else + automation1=`echo $automation1new | openssl enc -base64 -A` + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Custom VPN Server List Command entered for VPN Slot 1" >> $logfile + saveconfig + fi + ;; + + x1) + echo "" + echo -e "${CGreen}[Executing Script]${CClear}" + automation1unenc=$(echo "$automation1" | openssl enc -d -base64 -A) + echo -e "${CClear}Running: $automation1unenc" + echo "" + eval "$automation1unenc" > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + #Determine how many server entries are in each of the vpn slot alternate server files + if [ -f /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt ]; then + dlcnt=$(cat /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt | wc -l) >/dev/null 2>&1 + if [ $dlcnt -lt 1 ]; then + dlcnt=0 + elif [ -z $dlcnt ]; then + dlcnt=0 + fi + else + dlcnt=0 + fi + + if [ "$dlcnt" -gt 1 ] + then + cp "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" "/jffs/addons/vpnmon-r3.d/vr3svr1.txt" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source]${CClear}" + echo "" + echo -e "${CGreen}[Saved to VPN Client Slot 1 Server List]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom VPN Server List Command executed for VPN Slot 1" >> $logfile + echo "" + skynetwhitelist 1 + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + else + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source - Preserving Original Server List]${CClear}" + echo "" + echo -e "${CGreen}[Please check Query Language or VPN Service API may be down]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom VPN Client Server List Query for VPN Slot 1 yielded 0 rows -- Query may be invalid or VPN API service may be down" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + fi + ;; + + s1) + echo "" + echo -e "${CGreen}[Executing Skynet Whitelist Import]${CClear}" + firewall import whitelist /jffs/addons/vpnmon-r3.d/vr3svr1.txt "VPNMON-R3 VPN Slot 1 Import" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Contents of VPN Slot 1 Imported]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Skynet Whitelist imported for VPN Slot 1" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + ;; + + e2) + echo "" + if [ "$automation2" = "" ] || [ -z "$automation2" ]; then + echo -e "${CClear}Old Script: " + echo "" + else + echo -en "${CClear}Old Script: "; echo "$automation2" | openssl enc -d -base64 -A + echo "" + fi + read -rp 'Enter New Script (e=Exit): ' automation2new + if [ "$automation2new" = "" ] || [ -z "$automation2new" ]; then + automation2="" + saveconfig + elif [ "$automation2new" = "e" ]; then + echo ""; echo -e "${CGreen}[Exiting]${CClear}"; sleep 1 + else + automation2=`echo $automation2new | openssl enc -base64 -A` + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Custom VPN Server List Command entered for VPN Slot 2" >> $logfile + saveconfig + fi + ;; + + x2) + echo "" + echo -e "${CGreen}[Executing Script]${CClear}" + automation2unenc=$(echo "$automation2" | openssl enc -d -base64 -A) + echo -e "${CClear}Running: $automation2unenc" + echo "" + eval "$automation2unenc" > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + #Determine how many server entries are in each of the vpn slot alternate server files + if [ -f /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt ]; then + dlcnt=$(cat /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt | wc -l) >/dev/null 2>&1 + if [ $dlcnt -lt 1 ]; then + dlcnt=0 + elif [ -z $dlcnt ]; then + dlcnt=0 + fi + else + dlcnt=0 + fi + + if [ "$dlcnt" -gt 1 ] + then + cp "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" "/jffs/addons/vpnmon-r3.d/vr3svr2.txt" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source]${CClear}" + echo "" + echo -e "${CGreen}[Saved to VPN Client Slot 2 Server List]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom VPN Server List Command executed for VPN Slot 2" >> $logfile + echo "" + skynetwhitelist 2 + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + else + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source - Preserving Original Server List]${CClear}" + echo "" + echo -e "${CGreen}[Please check Query Language or VPN Service API may be down]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom VPN Client Server List Query for VPN Slot 2 yielded 0 rows -- Query may be invalid or VPN API service may be down" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + fi + ;; + + s2) + echo "" + echo -e "${CGreen}[Executing Skynet Whitelist Import]${CClear}" + firewall import whitelist /jffs/addons/vpnmon-r3.d/vr3svr2.txt "VPNMON-R3 VPN Slot 2 Import" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Contents of VPN Slot 2 Imported]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Skynet Whitelist imported for VPN Slot 2" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + ;; + + e3) + echo "" + if [ "$automation3" = "" ] || [ -z "$automation3" ]; then + echo -e "${CClear}Old Script: " + echo "" + else + echo -en "${CClear}Old Script: "; echo "$automation3" | openssl enc -d -base64 -A + echo "" + fi + read -rp 'Enter New Script (e=Exit): ' automation3new + if [ "$automation3new" = "" ] || [ -z "$automation3new" ]; then + automation3="" + saveconfig + elif [ "$automation3new" = "e" ]; then + echo ""; echo -e "${CGreen}[Exiting]${CClear}"; sleep 1 + else + automation3=`echo $automation3new | openssl enc -base64 -A` + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Custom VPN Server List Command entered for VPN Slot 3" >> $logfile + saveconfig + fi + ;; + + x3) + echo "" + echo -e "${CGreen}[Executing Script]${CClear}" + automation3unenc=$(echo "$automation3" | openssl enc -d -base64 -A) + echo -e "${CClear}Running: $automation3unenc" + echo "" + eval "$automation3unenc" > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + #Determine how many server entries are in each of the vpn slot alternate server files + if [ -f /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt ]; then + dlcnt=$(cat /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt | wc -l) >/dev/null 2>&1 + if [ $dlcnt -lt 1 ]; then + dlcnt=0 + elif [ -z $dlcnt ]; then + dlcnt=0 + fi + else + dlcnt=0 + fi + + if [ "$dlcnt" -gt 1 ] + then + cp "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" "/jffs/addons/vpnmon-r3.d/vr3svr3.txt" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source]${CClear}" + echo "" + echo -e "${CGreen}[Saved to VPN Client Slot 3 Server List]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom VPN Server List Command executed for VPN Slot 3" >> $logfile + echo "" + skynetwhitelist 3 + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + else + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source - Preserving Original Server List]${CClear}" + echo "" + echo -e "${CGreen}[Please check Query Language or VPN Service API may be down]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom VPN Client Server List Query for VPN Slot 3 yielded 0 rows -- Query may be invalid or VPN API service may be down" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + fi + ;; + + s3) + echo "" + echo -e "${CGreen}[Executing Skynet Whitelist Import]${CClear}" + firewall import whitelist /jffs/addons/vpnmon-r3.d/vr3svr3.txt "VPNMON-R3 VPN Slot 3 Import" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Contents of VPN Slot 3 Imported]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Skynet Whitelist imported for VPN Slot 3" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + ;; + + e4) + echo "" + if [ "$automation4" ="" ] || [ -z "$automation4" ]; then + echo -e "${CClear}Old Script: " + echo "" + else + echo -en "${CClear}Old Script: "; echo "$automation4" | openssl enc -d -base64 -A + echo "" + fi + read -rp 'Enter New Script (e=Exit): ' automation4new + if [ "$automation4new" = "" ] || [ -z "$automation4new" ]; then + automation4="" + saveconfig + elif [ "$automation4new" = "e" ]; then + echo ""; echo -e "${CGreen}[Exiting]${CClear}"; sleep 1 + else + automation4=`echo $automation4new | openssl enc -base64 -A` + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Custom VPN Server List Command entered for VPN Slot 4" >> $logfile + saveconfig + fi + ;; + + x4) + echo "" + echo -e "${CGreen}[Executing Script]${CClear}" + automation4unenc=$(echo "$automation4" | openssl enc -d -base64 -A) + echo -e "${CClear}Running: $automation4unenc" + echo "" + eval "$automation4unenc" > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + #Determine how many server entries are in each of the vpn slot alternate server files + if [ -f /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt ]; then + dlcnt=$(cat /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt | wc -l) >/dev/null 2>&1 + if [ $dlcnt -lt 1 ]; then + dlcnt=0 + elif [ -z $dlcnt ]; then + dlcnt=0 + fi + else + dlcnt=0 + fi + + if [ "$dlcnt" -gt 1 ] + then + cp "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" "/jffs/addons/vpnmon-r3.d/vr3svr4.txt" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source]${CClear}" + echo "" + echo -e "${CGreen}[Saved to VPN Client Slot 4 Server List]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom VPN Server List Command executed for VPN Slot 4" >> $logfile + echo "" + skynetwhitelist 4 + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + else + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source - Preserving Original Server List]${CClear}" + echo "" + echo -e "${CGreen}[Please check Query Language or VPN Service API may be down]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom VPN Client Server List Query for VPN Slot 4 yielded 0 rows -- Query may be invalid or VPN API service may be down" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + fi + ;; + + s4) + echo "" + echo -e "${CGreen}[Executing Skynet Whitelist Import]${CClear}" + firewall import whitelist /jffs/addons/vpnmon-r3.d/vr3svr4.txt "VPNMON-R3 VPN Slot 4 Import" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Contents of VPN Slot 4 Imported]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Skynet Whitelist imported for VPN Slot 4" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + ;; + + e5) + echo "" + if [ "$automation5" = "" ] || [ -z "$automation5" ]; then + echo -e "${CClear}Old Script: " + echo "" + else + echo -en "${CClear}Old Script: "; echo "$automation5" | openssl enc -d -base64 -A + echo "" + fi + read -rp 'Enter New Script (e=Exit): ' automation5new + if [ "$automation5new" = "" ] || [ -z "$automation5new" ]; then + automation5="" + saveconfig + elif [ "$automation5new" = "e" ]; then + echo ""; echo -e "${CGreen}[Exiting]${CClear}"; sleep 1 + else + automation5=`echo $automation5new | openssl enc -base64 -A` + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Custom VPN Server List Command entered for VPN Slot 5" >> $logfile + saveconfig + fi + ;; + + x5) + echo "" + echo -e "${CGreen}[Executing Script]${CClear}" + automation5unenc=$(echo "$automation5" | openssl enc -d -base64 -A) + echo -e "${CClear}Running: $automation5unenc" + echo "" + eval "$automation5unenc" > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + #Determine how many server entries are in each of the vpn slot alternate server files + if [ -f /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt ]; then + dlcnt=$(cat /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt | wc -l) >/dev/null 2>&1 + if [ $dlcnt -lt 1 ]; then + dlcnt=0 + elif [ -z $dlcnt ]; then + dlcnt=0 + fi + else + dlcnt=0 + fi + + if [ "$dlcnt" -gt 1 ] + then + cp "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" "/jffs/addons/vpnmon-r3.d/vr3svr5.txt" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source]${CClear}" + echo "" + echo -e "${CGreen}[Saved to VPN Client Slot 5 Server List]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom VPN Server List Command executed for VPN Slot 5" >> $logfile + echo "" + skynetwhitelist 5 + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + else + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source - Preserving Original Server List]${CClear}" + echo "" + echo -e "${CGreen}[Please check Query Language or VPN Service API may be down]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom VPN Client Server List Query for VPN Slot 5 yielded 0 rows -- Query may be invalid or VPN API service may be down" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + fi + ;; + + s5) + echo "" + echo -e "${CGreen}[Executing Skynet Whitelist Import]${CClear}" + firewall import whitelist /jffs/addons/vpnmon-r3.d/vr3svr5.txt "VPNMON-R3 VPN Slot 5 Import" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Contents of VPN Slot 5 Imported]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Skynet Whitelist imported for VPN Slot 5" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + ;; + + e6) + echo "" + if [ "$wgautomation1" = "" ] || [ -z "$wgautomation1" ]; then + echo -e "${CClear}Old Script: " + echo "" + else + echo -en "${CClear}Old Script: "; echo "$wgautomation1" | openssl enc -d -base64 -A + echo "" + fi + read -rp 'Enter New Script (e=Exit): ' wgautomation1new + if [ "$wgautomation1new" = "" ] || [ -z "$wgautomation1new" ]; then + wgautomation1="" + saveconfig + elif [ "$wgautomation1new" = "e" ]; then + echo ""; echo -e "${CGreen}[Exiting]${CClear}"; sleep 1 + else + wgautomation1=`echo $wgautomation1new | openssl enc -base64 -A` + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Custom WG Server List Command entered for WG Slot 1" >> $logfile + saveconfig + fi + ;; + + x6) + echo "" + echo -e "${CGreen}[Executing Script]${CClear}" + wgautomation1unenc=$(echo "$wgautomation1" | openssl enc -d -base64 -A) + echo -e "${CClear}Running: $wgautomation1unenc" + echo "" + eval "$wgautomation1unenc" > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + #Determine how many server entries are in each of the vpn slot alternate server files + if [ -f /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt ]; then + dlcnt=$(cat /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt | wc -l) >/dev/null 2>&1 + if [ $dlcnt -lt 1 ]; then + dlcnt=0 + elif [ -z $dlcnt ]; then + dlcnt=0 + fi + else + dlcnt=0 + fi + + if [ "$dlcnt" -gt 1 ] + then + cp "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" "/jffs/addons/vpnmon-r3.d/vr3wgsvr1.txt" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source]${CClear}" + echo "" + echo -e "${CGreen}[Saved to WG Client Slot 1 Server List]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom WG Server List Command executed for WG Slot 1" >> $logfile + echo "" + skynetwhitelist wg1 + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + else + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source - Preserving Original Server List]${CClear}" + echo "" + echo -e "${CGreen}[Please check Query Language or WG Service API may be down]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom WG Client Server List Query for WG Slot 1 yielded 0 rows -- Query may be invalid or WG API service may be down" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + fi + ;; + + s6) + echo "" + echo -e "${CGreen}[Executing Skynet Whitelist Import]${CClear}" + awk -F',' '{print $2}' /jffs/addons/vpnmon-r3.d/vr3wgsvr1.txt > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + firewall import whitelist /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt "VPNMON-R3 WG Slot 1 Import" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Contents of WG Slot 1 Imported]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Skynet Whitelist imported for WG Slot 1" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + ;; + + e7) + echo "" + if [ "$wgautomation2" = "" ] || [ -z "$wgautomation2" ]; then + echo -e "${CClear}Old Script: " + echo "" + else + echo -en "${CClear}Old Script: "; echo "$wgautomation2" | openssl enc -d -base64 -A + echo "" + fi + read -rp 'Enter New Script (e=Exit): ' wgautomation2new + if [ "$wgautomation2new" = "" ] || [ -z "$wgautomation2new" ]; then + wgautomation2="" + saveconfig + elif [ "$wgautomation2new" = "e" ]; then + echo ""; echo -e "${CGreen}[Exiting]${CClear}"; sleep 1 + else + wgautomation2=`echo $wgautomation2new | openssl enc -base64 -A` + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Custom WG Server List Command entered for WG Slot 2" >> $logfile + saveconfig + fi + ;; + + x7) + echo "" + echo -e "${CGreen}[Executing Script]${CClear}" + wgautomation2unenc=$(echo "$wgautomation2" | openssl enc -d -base64 -A) + echo -e "${CClear}Running: $wgautomation2unenc" + echo "" + eval "$wgautomation2unenc" > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + #Determine how many server entries are in each of the vpn slot alternate server files + if [ -f /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt ]; then + dlcnt=$(cat /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt | wc -l) >/dev/null 2>&1 + if [ $dlcnt -lt 1 ]; then + dlcnt=0 + elif [ -z $dlcnt ]; then + dlcnt=0 + fi + else + dlcnt=0 + fi + + if [ "$dlcnt" -gt 1 ] + then + cp "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" "/jffs/addons/vpnmon-r3.d/vr3wgsvr2.txt" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source]${CClear}" + echo "" + echo -e "${CGreen}[Saved to WG Client Slot 2 Server List]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom WG Server List Command executed for WG Slot 2" >> $logfile + echo "" + skynetwhitelist wg2 + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + else + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source - Preserving Original Server List]${CClear}" + echo "" + echo -e "${CGreen}[Please check Query Language or WG Service API may be down]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom WG Client Server List Query for WG Slot 2 yielded 0 rows -- Query may be invalid or WG API service may be down" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + fi + ;; + + s7) + echo "" + echo -e "${CGreen}[Executing Skynet Whitelist Import]${CClear}" + awk -F',' '{print $2}' /jffs/addons/vpnmon-r3.d/vr3wgsvr2.txt > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + firewall import whitelist /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt "VPNMON-R3 WG Slot 2 Import" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Contents of WG Slot 2 Imported]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Skynet Whitelist imported for WG Slot 2" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + ;; + + e8) + echo "" + if [ "$wgautomation3" = "" ] || [ -z "$wgautomation3" ]; then + echo -e "${CClear}Old Script: " + echo "" + else + echo -en "${CClear}Old Script: "; echo "$wgautomation3" | openssl enc -d -base64 -A + echo "" + fi + read -rp 'Enter New Script (e=Exit): ' wgautomation3new + if [ "$wgautomation3new" = "" ] || [ -z "$wgautomation3new" ]; then + wgautomation3="" + saveconfig + elif [ "$wgautomation3new" = "e" ]; then + echo ""; echo -e "${CGreen}[Exiting]${CClear}"; sleep 1 + else + wgautomation3=`echo $wgautomation3new | openssl enc -base64 -A` + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Custom WG Server List Command entered for WG Slot 3" >> $logfile + saveconfig + fi + ;; + + x8) + echo "" + echo -e "${CGreen}[Executing Script]${CClear}" + wgautomation3unenc=$(echo "$wgautomation3" | openssl enc -d -base64 -A) + echo -e "${CClear}Running: $wgautomation3unenc" + echo "" + eval "$wgautomation3unenc" > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + #Determine how many server entries are in each of the vpn slot alternate server files + if [ -f /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt ]; then + dlcnt=$(cat /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt | wc -l) >/dev/null 2>&1 + if [ $dlcnt -lt 1 ]; then + dlcnt=0 + elif [ -z $dlcnt ]; then + dlcnt=0 + fi + else + dlcnt=0 + fi + + if [ "$dlcnt" -gt 1 ] + then + cp "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" "/jffs/addons/vpnmon-r3.d/vr3wgsvr3.txt" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source]${CClear}" + echo "" + echo -e "${CGreen}[Saved to WG Client Slot 3 Server List]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom WG Server List Command executed for WG Slot 3" >> $logfile + echo "" + skynetwhitelist wg3 + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + else + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source - Preserving Original Server List]${CClear}" + echo "" + echo -e "${CGreen}[Please check Query Language or WG Service API may be down]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom WG Client Server List Query for WG Slot 3 yielded 0 rows -- Query may be invalid or WG API service may be down" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + fi + ;; + + s8) + echo "" + echo -e "${CGreen}[Executing Skynet Whitelist Import]${CClear}" + awk -F',' '{print $2}' /jffs/addons/vpnmon-r3.d/vr3wgsvr3.txt > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + firewall import whitelist /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt "VPNMON-R3 WG Slot 3 Import" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Contents of WG Slot 3 Imported]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Skynet Whitelist imported for WG Slot 3" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + ;; + + e9) + echo "" + if [ "$wgautomation4" = "" ] || [ -z "$wgautomation4" ]; then + echo -e "${CClear}Old Script: " + echo "" + else + echo -en "${CClear}Old Script: "; echo "$wgautomation4" | openssl enc -d -base64 -A + echo "" + fi + read -rp 'Enter New Script (e=Exit): ' wgautomation4new + if [ "$wgautomation4new" = "" ] || [ -z "$wgautomation4new" ]; then + wgautomation4="" + saveconfig + elif [ "$wgautomation4new" = "e" ]; then + echo ""; echo -e "${CGreen}[Exiting]${CClear}"; sleep 1 + else + wgautomation4=`echo $wgautomation4new | openssl enc -base64 -A` + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Custom WG Server List Command entered for WG Slot 4" >> $logfile + saveconfig + fi + ;; + + x9) + echo "" + echo -e "${CGreen}[Executing Script]${CClear}" + wgautomation4unenc=$(echo "$wgautomation4" | openssl enc -d -base64 -A) + echo -e "${CClear}Running: $wgautomation4unenc" + echo "" + eval "$wgautomation4unenc" > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + #Determine how many server entries are in each of the vpn slot alternate server files + if [ -f /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt ]; then + dlcnt=$(cat /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt | wc -l) >/dev/null 2>&1 + if [ $dlcnt -lt 1 ]; then + dlcnt=0 + elif [ -z $dlcnt ]; then + dlcnt=0 + fi + else + dlcnt=0 + fi + + if [ "$dlcnt" -gt 1 ] + then + cp "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" "/jffs/addons/vpnmon-r3.d/vr3wgsvr4.txt" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source]${CClear}" + echo "" + echo -e "${CGreen}[Saved to WG Client Slot 4 Server List]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom WG Server List Command executed for WG Slot 4" >> $logfile + echo "" + skynetwhitelist wg4 + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + else + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source - Preserving Original Server List]${CClear}" + echo "" + echo -e "${CGreen}[Please check Query Language or WG Service API may be down]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom WG Client Server List Query for WG Slot 4 yielded 0 rows -- Query may be invalid or WG API service may be down" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + fi + ;; + + s9) + echo "" + echo -e "${CGreen}[Executing Skynet Whitelist Import]${CClear}" + awk -F',' '{print $2}' /jffs/addons/vpnmon-r3.d/vr3wgsvr4.txt > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + firewall import whitelist /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt "VPNMON-R3 WG Slot 4 Import" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Contents of WG Slot 4 Imported]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Skynet Whitelist imported for WG Slot 4" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + ;; + + e0) + echo "" + if [ "$wgautomation5" = "" ] || [ -z "$wgautomation5" ]; then + echo -e "${CClear}Old Script: " + echo "" + else + echo -en "${CClear}Old Script: "; echo "$wgautomation5" | openssl enc -d -base64 -A + echo "" + fi + read -rp 'Enter New Script (e=Exit): ' wgautomation5new + if [ "$wgautomation5new" = "" ] || [ -z "$wgautomation5new" ]; then + wgautomation5="" + saveconfig + elif [ "$wgautomation5new" = "e" ]; then + echo ""; echo -e "${CGreen}[Exiting]${CClear}"; sleep 1 + else + wgautomation5=`echo $wgautomation5new | openssl enc -base64 -A` + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Custom WG Server List Command entered for WG Slot 5" >> $logfile + saveconfig + fi + ;; + + x0) + echo "" + echo -e "${CGreen}[Executing Script]${CClear}" + wgautomation5unenc=$(echo "$wgautomation5" | openssl enc -d -base64 -A) + echo -e "${CClear}Running: $wgautomation5unenc" + echo "" + eval "$wgautomation5unenc" > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + #Determine how many server entries are in each of the vpn slot alternate server files + if [ -f /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt ]; then + dlcnt=$(cat /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt | wc -l) >/dev/null 2>&1 + if [ $dlcnt -lt 1 ]; then + dlcnt=0 + elif [ -z $dlcnt ]; then + dlcnt=0 + fi + else + dlcnt=0 + fi + + if [ "$dlcnt" -gt 1 ] + then + cp "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" "/jffs/addons/vpnmon-r3.d/vr3wgsvr5.txt" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source]${CClear}" + echo "" + echo -e "${CGreen}[Saved to WG Client Slot 5 Server List]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom WG Server List Command executed for WG Slot 5" >> $logfile + echo "" + skynetwhitelist wg5 + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + else + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source - Preserving Original Server List]${CClear}" + echo "" + echo -e "${CGreen}[Please check Query Language or WG Service API may be down]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom WG Client Server List Query for WG Slot 5 yielded 0 rows -- Query may be invalid or WG API service may be down" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + fi + ;; + + s0) + echo "" + echo -e "${CGreen}[Executing Skynet Whitelist Import]${CClear}" + awk -F',' '{print $2}' /jffs/addons/vpnmon-r3.d/vr3wgsvr5.txt > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + firewall import whitelist /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt "VPNMON-R3 WG Slot 5 Import" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo "" + echo -e "${CGreen}[Contents of WG Slot 5 Imported]${CClear}" + echo "" + echo -e "${CGreen}[Execution Complete]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Skynet Whitelist imported for WG Slot 5" >> $logfile + echo "" + read -rsp $'Press any key to acknowledge...\n' -n1 key + ;; + + [Ee]) + timer="$timerloop" + break;; + + esac + fi + +done + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# timerloopconfig lets you configure how long you want the timer cycle to last between vpn connection checks + +##----------------------------------------## +## Modified by Martinski W. [2024-Nov-02] ## +##----------------------------------------## +timerloopconfig() +{ + +while true +do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Timer Loop Configuration ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate how long the timer cycle should take between VPN Connection checks.${CClear}" + echo -e "${InvGreen} ${CClear} (Default = 60 seconds)${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Current: ${CGreen}$timerloop sec${CClear}" + echo + read -p "Please enter value in seconds [5-999] (e=Exit): " newTimerLoop + if [ -z "$newTimerLoop" ] || echo "$newTimerLoop" | grep -qE "^(e|E)$" + then + if echo "$timerloop" | grep -qE "^([1-9][0-9]{0,2})$" && \ + [ "$timerloop" -ge 5 ] && [ "$timerloop" -le 999 ] + then + timer="$timerloop" + printf "\n${CClear}[Exiting]\n" + sleep 1 ; break + else + printf "\n${CRed}*ERROR*: Please enter a valid number between 5 and 999.${CClear}\n" + sleep 3 + fi + elif echo "$newTimerLoop" | grep -qE "^([1-9][0-9]{0,2})$" && \ + [ "$newTimerLoop" -ge 5 ] && [ "$newTimerLoop" -le 999 ] + then + timerloop="$newTimerLoop" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Timer Loop Configuration saved" >> $logfile + saveconfig + timer="$timerloop" + printf "\n${CClear}[OK]\n" + sleep 1 ; break + else + printf "\n${CRed}*ERROR*: Please enter a valid number between 5 and 999.${CClear}\n" + sleep 3 + fi +done + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# maxping lets you configure how high the vpn tunnel ping will get before it's reset + +##----------------------------------------## +## Modified by Martinski W. [2024-Oct-05] ## +##----------------------------------------## +maxping() +{ + +while true +do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Maximum Ping Before Reset ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate how high the PING can be across your VPN tunnel before resetting the${CClear}" + echo -e "${InvGreen} ${CClear} connection in favor of a different server with a lower PING? A connection with a${CClear}" + echo -e "${InvGreen} ${CClear} lower PING may provide better network performance and less latency.${CClear}" + echo -e "${InvGreen} ${CClear} (Default = 500 milliseconds, Disabled = 0)${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Current: ${CGreen}$pingreset ms${CClear}" + echo + read -p "Please enter numeric value (0-999)? (0=Disabled, e=Exit): " newPingReset + if echo "$newPingReset" | grep -qE "^(0|[1-9][0-9]{0,2})$" && \ + [ "$newPingReset" -ge 0 ] && [ "$newPingReset" -le 999 ] + then + pingreset="$newPingReset" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: PING Reset Configuration saved" >> $logfile + saveconfig + elif [ -z "$newPingReset" ] || echo "$newPingReset" | grep -qE "^(E|e)$" + then + echo ; echo -e "${CClear}[Exiting]" + timer="$timerloop" + sleep 2 + break + else + echo + echo -e "${CClear}[Please enter value between 0 and 999, e=Exit]" + sleep 3 + fi +done +} + +##-------------------------------------## +## Added by Martinski W. [2024-Oct-06] ## +##-------------------------------------## +_ValidateCronJobHour_() +{ + if [ $# -eq 0 ] || [ -z "$1" ] ; then return 1 ; fi + if echo "$1" | grep -qE "^(0|[1-9][0-9]?)$" && \ + [ "$1" -ge 0 ] && [ "$1" -lt 24 ] + then return 0 ; else return 1 ; fi +} + +_ValidateCronJobMinute_() +{ + if [ $# -eq 0 ] || [ -z "$1" ] ; then return 1 ; fi + if echo "$1" | grep -qE "^(0|[1-9][0-9]?)$" && \ + [ "$1" -ge 0 ] && [ "$1" -lt 60 ] + then return 0 ; else return 1 ; fi +} + +# ------------------------------------------------------------------------------------------------------------------------- +# schedulevpnreset lets you enable and set a time for a scheduled daily vpn reset + +##----------------------------------------## +## Modified by Martinski W. [2024-Oct-06] ## +##----------------------------------------## +schedulevpnreset() +{ + +while true +do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} VPN Reset Scheduler ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate below if you would like to enable and schedule a daily VPN Reset CRON" + echo -e "${InvGreen} ${CClear} job. This will reset each monitored VPN connection. (Default = Disabled)" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo -e "${InvGreen} ${CClear}" + if [ "$schedule" = "0" ] + then + echo -e "${InvGreen} ${CClear} Current: ${CRed}Disabled${CClear}" + elif [ "$schedule" = "1" ] + then + schedhrs="$(awk "BEGIN {printf \"%02.f\",${schedulehrs}}")" + schedmin="$(awk "BEGIN {printf \"%02.f\",${schedulemin}}")" + schedtime="${CGreen}$schedhrs:$schedmin${CClear}" + echo -e "${InvGreen} ${CClear} Current: ${CGreen}Enabled, Daily @ $schedtime${CClear}" + fi + echo + read -p 'Schedule Daily Reset? (0=No, 1=Yes, e=Exit): ' newSchedule + if [ -z "$newSchedule" ] ; then newSchedule="${schedule:=0}" ; fi + + if [ "$newSchedule" = "0" ] + then + schedule=0 + if [ -f /jffs/scripts/services-start ] + then + sed -i -e '/vpnmon-r3.sh/d' /jffs/scripts/services-start + cru d RunVPNMONR3reset + schedulehrs=1 + schedulemin=0 + echo "" + echo -e "${CGreen}[Modifiying SERVICES-START file]..." + sleep 2 + echo "" + echo -e "${CGreen}[Modifying CRON jobs]..." + sleep 2 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN Reset Schedule Disabled" >> $logfile + saveconfig + timer="$timerloop" + break + fi + + elif [ "$newSchedule" = "1" ] + then + schedule=1 + echo + echo -e "${InvGreen} ${InvDkGray}${CWhite} VPN Reset Scheduler ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate below what time you would like to schedule a daily VPN Reset CRON" + echo -e "${InvGreen} ${CClear} job. (Default = 1 hr, 0 min = 01:00 = 1:00am)" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo + read -p 'Schedule HOURS [0-23]?: ' newScheduleHrs + if [ -z "$newScheduleHrs" ] + then + if _ValidateCronJobHour_ "$schedulehrs" + then scheduleHrsOK=true + else scheduleHrsOK=false + fi + elif _ValidateCronJobHour_ "$newScheduleHrs" + then + scheduleHrsOK=true + schedulehrs="$newScheduleHrs" + else + scheduleHrsOK=false + schedulehrs="${schedulehrs:=1}" + printf "${CRed}*ERROR*: INVALID Entry.${CClear}\n\n" + fi + read -p 'Schedule MINUTES [0-59]?: ' newScheduleMins + if [ -z "$newScheduleMins" ] + then + if _ValidateCronJobMinute_ "$schedulemin" + then scheduleMinsOK=true + else scheduleMinsOK=false + fi + elif _ValidateCronJobMinute_ "$newScheduleMins" + then + scheduleMinsOK=true + schedulemin="$newScheduleMins" + else + scheduleMinsOK=false + schedulemin="${schedulemin:=0}" + printf "${CRed}*ERROR*: INVALID Entry.${CClear}\n" + fi + if ! "$scheduleHrsOK" || ! "$scheduleMinsOK" + then + doResetSave=false + if ! "$scheduleHrsOK" && ! _ValidateCronJobHour_ "$schedulehrs" + then schedulehrs=1 ; doResetSave=true + fi + if ! "$scheduleMinsOK" && ! _ValidateCronJobMinute_ "$schedulemin" + then schedulemin=0 ; doResetSave=true + fi + if "$doResetSave" + then + schedule=0 + saveconfig + printf "\n${CRed}INVALID input found. Resetting values.${CClear}\n\n" + else + printf "\n${CRed}INVALID input found. No changes made.${CClear}\n\n" + fi + echo -e "${CClear}[Exiting]" + timer="$timerloop" + sleep 3 + break + fi + echo + echo -e "${CGreen}[Modifying SERVICES-START file]..." + sleep 2 + + if [ -f /jffs/scripts/services-start ] + then + if ! grep -q -F "sh /jffs/scripts/vpnmon-r3.sh -reset" /jffs/scripts/services-start + then + echo 'cru a RunVPNMONR3reset "'"$schedulemin $schedulehrs * * * sh /jffs/scripts/vpnmon-r3.sh -reset"'"' >> /jffs/scripts/services-start + cru a RunVPNMONR3reset "$schedulemin $schedulehrs * * * sh /jffs/scripts/vpnmon-r3.sh -reset" + else + #delete and re-add if it already exists in case there's a time change + sed -i -e '/vpnmon-r3.sh/d' /jffs/scripts/services-start + cru d RunVPNMONR3reset + echo 'cru a RunVPNMONR3reset "'"$schedulemin $schedulehrs * * * sh /jffs/scripts/vpnmon-r3.sh -reset"'"' >> /jffs/scripts/services-start + cru a RunVPNMONR3reset "$schedulemin $schedulehrs * * * sh /jffs/scripts/vpnmon-r3.sh -reset" + fi + else + echo 'cru a RunVPNMONR3reset "'"$schedulemin $schedulehrs * * * sh /jffs/scripts/vpnmon-r3.sh -reset"'"' >> /jffs/scripts/services-start + chmod 755 /jffs/scripts/services-start + cru a RunVPNMONR3reset "$schedulemin $schedulehrs * * * sh /jffs/scripts/vpnmon-r3.sh -reset" + fi + + echo + echo -e "${CGreen}[Modifying CRON jobs]..." + sleep 2 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN Reset Schedule Enabled" >> $logfile + saveconfig + timer="$timerloop" + break + + elif [ "$newSchedule" = "e" ] + then + echo ; echo -e "${CClear}[Exiting]" + timer="$timerloop" + sleep 2 + break + else + schedule="${schedule:=0}" + schedulehrs="${schedulehrs:=1}" + schedulemin="${schedulemin:=0}" + saveconfig + fi + +done +} + +# ------------------------------------------------------------------------------------------------------------------------- +# autostart lets you enable the ability for vpnmon-r3 to autostart after a router reboot + +autostart() +{ + +while true; do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Reboot Protection ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate below if you would like to enable VPNMON-R3 to autostart after a" + echo -e "${InvGreen} ${CClear} router reboot. This will ensure continued, uninterrupted VPN connection monitoring." + echo -e "${InvGreen} ${CClear} (Default = Disabled)" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo -e "${InvGreen} ${CClear}" + if [ "$autostart" = "0" ] + then + echo -e "${InvGreen} ${CClear} Current: ${CRed}Disabled${CClear}" + elif [ "$autostart" = "1" ] + then + echo -e "${InvGreen} ${CClear} Current: ${CGreen}Enabled${CClear}" + fi + echo + read -p 'Enable Reboot Protection? (0=No, 1=Yes, e=Exit): ' newAutoStart + # Use default value on enter keypress or invalid input # + if [ -z "$newAutoStart" ] ; then newAutoStart="${autostart:=0}" ; fi + + if [ "$newAutoStart" = "0" ] + then + autostart=0 + if [ -f /jffs/scripts/post-mount ] + then + sed -i -e '/vpnmon-r3.sh/d' /jffs/scripts/post-mount + echo "" + echo -e "${CGreen}[Modifying POST-MOUNT file]..." + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Reboot Protection Disabled" >> $logfile + saveconfig + sleep 2 + timer="$timerloop" + break + fi + + elif [ "$newAutoStart" = "1" ] + then + autostart=1 + if [ -f /jffs/scripts/post-mount ] + then + if ! grep -q -F "(sleep 30 && /jffs/scripts/vpnmon-r3.sh -screen) & # Added by vpnmon-r3" /jffs/scripts/post-mount + then + echo "(sleep 30 && /jffs/scripts/vpnmon-r3.sh -screen) & # Added by vpnmon-r3" >> /jffs/scripts/post-mount + echo + echo -e "${CGreen}[Modifying POST-MOUNT file]..." + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Reboot Protection Enabled" >> $logfile + saveconfig + sleep 2 + timer="$timerloop" + break + else + saveconfig + sleep 1 + fi + + else + echo "#!/bin/sh" > /jffs/scripts/post-mount + echo "" >> /jffs/scripts/post-mount + echo "(sleep 30 && /jffs/scripts/vpnmon-r3.sh -screen) & # Added by vpnmon-r3" >> /jffs/scripts/post-mount + chmod 755 /jffs/scripts/post-mount + echo + echo -e "${CGreen}[Modifying POST-MOUNT file]..." + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Reboot Protection Enabled" >> $logfile + saveconfig + sleep 2 + timer="$timerloop" + break + fi + + elif [ "$newAutoStart" = "e" ] + then + echo ; echo -e "${CClear}[Exiting]" + timer="$timerloop" + sleep 2 + break + else + autostart="${autostart:=0}" + saveconfig + fi + +done +} + +# ------------------------------------------------------------------------------------------------------------------------- +# trimlogs will cut down log size (in rows) based on custom value + +trimlogs() +{ + if [ "$logsize" -gt 0 ] + then + currlogsize="$(wc -l $logfile | awk '{ print $1 }')" # Determine the number of rows in the log + + if [ "$currlogsize" -gt "$logsize" ] # If it's bigger than the max allowed, tail/trim it! + then + echo "$(tail -$logsize $logfile)" > $logfile + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Trimmed the log file down to $logsize lines" >> $logfile + fi + fi +} + +# ------------------------------------------------------------------------------------------------------------------------- +# saveconfig saves the vpnmon-r3.cfg file after every major change, and applies that to the script on the fly + +saveconfig() +{ + { echo 'availableslots="'"$availableslots"'"' + echo 'PINGHOST="'"$PINGHOST"'"' + echo 'logsize='$logsize + echo 'timerloop='$timerloop + echo 'schedule='$schedule + echo 'schedulehrs='$schedulehrs + echo 'schedulemin='$schedulemin + echo 'autostart='$autostart + echo 'pingreset='$pingreset + echo 'automation1="'"$automation1"'"' + echo 'automation2="'"$automation2"'"' + echo 'automation3="'"$automation3"'"' + echo 'automation4="'"$automation4"'"' + echo 'automation5="'"$automation5"'"' + echo 'wgautomation1="'"$wgautomation1"'"' + echo 'wgautomation2="'"$wgautomation2"'"' + echo 'wgautomation3="'"$wgautomation3"'"' + echo 'wgautomation4="'"$wgautomation4"'"' + echo 'wgautomation5="'"$wgautomation5"'"' + echo 'refreshserverlists='$refreshserverlists + echo 'unboundclient='$unboundclient + echo 'unboundshowip='$unboundshowip + echo 'monitorwan='$monitorwan + echo 'useovpn='$useovpn + echo 'usewg='$usewg + echo 'updateskynet='$updateskynet + echo 'amtmemailsuccess='$amtmemailsuccess + echo 'amtmemailfailure='$amtmemailfailure + echo 'rstspdmerlin='$rstspdmerlin + echo 'ratelimit='$ratelimit + } > "$config" + + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New vpnmon-r3.cfg File Saved" >> $logfile +} + +# ------------------------------------------------------------------------------------------------------------------------- +# VPN_GetClientState was created by @Martinski in many thanks to trying to eliminate unknown operand errors due to null +# vpn_clientX_state values + +_VPN_GetClientState_() +{ + if [ $# -lt 1 ] || [ -z "$1" ] || ! echo "$1" | grep -qE "^[1-5]$" + then echo "**ERROR**" ; return 1 ; fi + + local nvramVal="$($timeoutcmd$timeoutsec nvram get "vpn_client${1}_state")" + if [ -z "$nvramVal" ] || ! echo "$nvramVal" | grep -qE "^[+-]?[0-9]$" + then echo "0" ; else echo "$nvramVal" ; fi + return 0 +} + +# ------------------------------------------------------------------------------------------------------------------------- +# WG_GetClientState is based off _VPN_GetClientState_ + +_WG_GetClientState_() +{ + if [ $# -lt 1 ] || [ -z "$1" ] || ! echo "$1" | grep -qE "^[1-5]$" + then echo "**ERROR**" ; return 1 ; fi + + # Inspiration from ZebMcKayHan's WGC Watchdog Script + last_handshake=$(wg show wgc$1 latest-handshakes | awk '{print $2}') >/dev/null 2>&1 + + if [ -z $last_handshake ] + then + WGnvramVal=0 #disconnected + else + WGnvramVal=2 #connected + fi + + #local WGnvramVal="$($timeoutcmd$timeoutsec nvram get "wgc${1}_enable")" + if [ -z "$WGnvramVal" ] || ! echo "$WGnvramVal" | grep -qE "^[+-]?[0-9]$" + then echo "0" ; else echo "$WGnvramVal" ; fi + return 0 +} + +# ------------------------------------------------------------------------------------------------------------------------- + +######################################################################## +# AMTM Email Notification Functionality generously donated by @Martinski! +# +# Creation Date: 2020-Jun-11 [Martinski W.] +# Last Modified: 2024-Feb-07 [Martinski W.] +# Modified for VPNMON-R3 Purposes [Viktor Jaep] +######################################################################## + +#-----------------------------------------------------------# +_DownloadCEMLibraryFile_() +{ + local msgStr retCode + case "$1" in + update) msgStr="Updating" ;; + install) msgStr="Installing" ;; + *) return 1 ;; + esac + + printf "\33[2K\r" + printf "${CGreen}\r[INFO: ${msgStr} the shared AMTM email library script file to support email notifications...]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: ${msgStr} the shared AMTM email library script file to support email notifications..." >> $logfile + + mkdir -m 755 -p "$CUSTOM_EMAIL_LIBDir" + curl -kLSs --retry 3 --retry-delay 5 --retry-connrefused \ + "${CEM_LIB_URL}/$CUSTOM_EMAIL_LIBName" -o "$CUSTOM_EMAIL_LIBFile" + curlCode="$?" + + if [ "$curlCode" -eq 0 ] && [ -f "$CUSTOM_EMAIL_LIBFile" ] + then + retCode=0 + chmod 755 "$CUSTOM_EMAIL_LIBFile" + . "$CUSTOM_EMAIL_LIBFile" + #printf "\nDone.\n" + else + retCode=1 + printf "\33[2K\r" + printf "${CRed}\r[ERROR: Unable to download the shared library script file ($CUSTOM_EMAIL_LIBName).]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - **ERROR**: Unable to download the shared AMTM email library script file [$CUSTOM_EMAIL_LIBName]." >> $logfile + fi + return "$retCode" +} + +#-----------------------------------------------------------# +# ARG1: The email name/alias to be used as "FROM_NAME" +# ARG2: The email Subject string. +# ARG3: Full path of file containing the email Body text. +# ARG4: The email Body Title string [OPTIONAL]. +#-----------------------------------------------------------# +_SendEMailNotification_() +{ + if [ -z "${amtmIsEMailConfigFileEnabled:+xSETx}" ] + then + printf "\33[2K\r" + printf "${CRed}\r[ERROR: Email library script ($CUSTOM_EMAIL_LIBFile) *NOT* FOUND.]${CClear}" + sleep 5 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - **ERROR**: Email library script [$CUSTOM_EMAIL_LIBFile] *NOT* FOUND." >> $logfile + return 1 + fi + + if [ $# -lt 3 ] || [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ] + then + printf "\33[2K\r" + printf "${CRed}\r[ERROR: INSUFFICIENT email parameters]${CClear}" + sleep 5 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - **ERROR**: INSUFFICIENT email parameters." >> $logfile + return 1 + fi + local retCode emailBodyTitleStr="" + + [ $# -gt 3 ] && [ -n "$4" ] && emailBodyTitleStr="$4" + + FROM_NAME="$1" + _SendEMailNotification_CEM_ "$2" "-F=$3" "$emailBodyTitleStr" + retCode="$?" + + if [ "$retCode" -eq 0 ] + then + printf "\33[2K\r" + printf "${CGreen}\r[Email notification was sent successfully ($2)]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Email notification was sent successfully [$2]" >> $logfile + sleep 5 + else + printf "\33[2K\r" + printf "${CRed}\r[ERROR: Failure to send email notification (Error Code: $retCode - $2).]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - **ERROR**: Failure to send email notification [$2]" >> $logfile + sleep 5 + fi + + return "$retCode" +} + +# ------------------------------------------------------------------------------------------------------------------------- +# sendmessage is a function that sends an AMTM email based on activity within VPNMON-R3 +# $1 = Success/Failure 0/1 +# $2 = Component +# $3 = VPN Slot + +sendmessage() +{ + + #If AMTM email functionality is disabled, return back to the function call + if [ "$amtmemailsuccess" = "0" ] && [ "$amtmemailfailure" = "0" ]; then + return + fi + + #Load, install or update the shared AMTM Email integration library + if [ -f "$CUSTOM_EMAIL_LIBFile" ] + then + . "$CUSTOM_EMAIL_LIBFile" + + if [ -z "${CEM_LIB_VERSION:+xSETx}" ] || \ + _CheckLibraryUpdates_CEM_ "$CUSTOM_EMAIL_LIBDir" quiet + then + _DownloadCEMLibraryFile_ "update" + fi + else + _DownloadCEMLibraryFile_ "install" + fi + + cemIsFormatHTML=true + cemIsVerboseMode=false + tmpEMailBodyFile="/tmp/var/tmp/tmpEMailBody_${scriptFileNTag}.$$.TXT" + + ratelimiter + emaillimit="$?" + if [ "$emaillimit" -eq 0 ] + then + + #Pick the scenario and send email + if [ "$1" = "1" ] && [ "$amtmemailfailure" = "1" ]; then + if [ "$2" = "Recovering from WAN Down" ]; then + emailSubject="ALERT: Router Recovering from WAN Down" + emailBodyTitle="ALERT: Router Recovering from WAN Down" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "ALERT: VPNMON-R3 is currently recovering from a WAN Down Situation!\n" + printf "Router has detected a WAN Link/Modem and waited 300 seconds for general network.\n" + printf "connectivity to stabilize before re-establishing VPN connectivity.\n" + printf "\n" + } > "$tmpEMailBodyFile" + elif [ "$2" = "VPN Slot In Error State" ]; then + emailSubject="FAILURE: VPN Slot $3 in Error State" + emailBodyTitle="FAILURE: VPN Slot $3 in Error State" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "FAILURE: VPNMON-R3 has detected that VPN Slot $3 is in an error state. VPN Slot $3 has been reset.\n" + printf "Please check your network environment and configuration if this error continues to persist." + printf "\n" + } > "$tmpEMailBodyFile" + elif [ "$2" = "VPN Tunnel Disconnected" ]; then + emailSubject="FAILURE: VPN Slot $3 has Disconnected" + emailBodyTitle="FAILURE: VPN Slot $3 has Disconnected" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "FAILURE: VPNMON-R3 has detected that VPN Slot $3 has disconnected. VPN Slot $3 has been reset.\n" + printf "Please check your network environment and configuration if this error continues to persist." + printf "\n" + } > "$tmpEMailBodyFile" + elif [ "$2" = "VPN Slot Is Non-Responsive" ]; then + emailSubject="FAILURE: VPN Slot $3 is Non-Responsive" + emailBodyTitle="FAILURE: VPN Slot $3 is Non-Responsive" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "FAILURE: VPNMON-R3 has detected that VPN Slot $3 is non-responsive. VPN Slot $3 has been reset.\n" + printf "Please check your network environment and configuration if this error continues to persist." + printf "\n" + } > "$tmpEMailBodyFile" + elif [ "$2" = "VPN Slot Exceeded Max Ping" ]; then + emailSubject="WARNING: VPN Slot $3 Exceeded Max Ping" + emailBodyTitle="WARNING: VPN Slot $3 Exceeded Max Ping" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "WARNING: VPNMON-R3 has detected that VPN Slot $3 exceeded max ping. VPN Slot $3 has been reset.\n" + printf "Please check your network environment and configuration if this error continues to persist." + printf "\n" + } > "$tmpEMailBodyFile" + elif [ "$2" = "VPN Slot Not Synced With Unbound" ]; then + emailSubject="WARNING: VPN Slot $3 Not Synced with Unbound" + emailBodyTitle="WARNING: VPN Slot $3 Not Synced with Unbound" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "WARNING: VPNMON-R3 has detected that VPN Slot $3 is not synced with Unbound. VPN Slot $3 has been reset.\n" + printf "Please check your network environment and configuration if this error continues to persist." + printf "\n" + } > "$tmpEMailBodyFile" + ##-------------------------------------## + ## Modified by Dan G. [2025-Jul-16] ## + ##-------------------------------------## + elif [ "$2" = "VPN Server List Query Yielded 0 Rows" ]; then + emailSubject="WARNING: VPN Slot $3 Server List Query Yielded 0 Rows" + emailBodyTitle="WARNING: VPN Slot $3 Server List Query Yielded 0 Rows" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "WARNING: VPNMON-R3 has detected that the Custom Server List Query for VPN Slot $3 yielded 0 results.\n" + printf "This may be due to an error in the query, or the VPN provider API service may be down or unreachable." + printf "Please check your network environment and configuration if this error continues to persist." + printf "\n" + } > "$tmpEMailBodyFile" + elif [ "$2" = "WG Server List Query Yielded 0 Rows" ]; then + emailSubject="WARNING: WG Slot $3 Server List Query Yielded 0 Rows" + emailBodyTitle="WARNING: WG Slot $3 Server List Query Yielded 0 Rows" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "WARNING: VPNMON-R3 has detected that the Custom Server List Query for WG Slot $3 yielded 0 results.\n" + printf "This may be due to an error in the query, or the VPN provider API service may be down or unreachable." + printf "Please check your network environment and configuration if this error continues to persist." + printf "\n" + } > "$tmpEMailBodyFile" + elif [ "$2" = "WG Slot Exceeded Max Ping" ]; then + emailSubject="WARNING: WG Slot $3 Exceeded Max Ping" + emailBodyTitle="WARNING: WG Slot $3 Exceeded Max Ping" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "WARNING: VPNMON-R3 has detected that WG Slot $3 exceeded max ping. WG Slot $3 has been reset.\n" + printf "Please check your network environment and configuration if this error continues to persist." + printf "\n" + } > "$tmpEMailBodyFile" + elif [ "$2" = "WG Handshake Exceeded" ]; then + emailSubject="WARNING: WG Slot $3 Exceeded Handshake" + emailBodyTitle="WARNING: WG Slot $3 Exceeded Handshake" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "WARNING: VPNMON-R3 has detected that WG Slot $3 exceeded a 200s handshake. WG Slot $3 has been reset.\n" + printf "Please check your network environment and configuration if this error continues to persist." + printf "\n" + } > "$tmpEMailBodyFile" + elif [ "$2" = "WG Tunnel Disconnected" ]; then + emailSubject="WARNING: WG Slot $3 Tunnel Disconnected" + emailBodyTitle="WARNING: WG Slot $3 Tunnel Disconnected" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "WARNING: VPNMON-R3 has detected that WG Slot $3 tunnel disconnected. WG Slot $3 has been reset.\n" + printf "Please check your network environment and configuration if this error continues to persist." + printf "\n" + } > "$tmpEMailBodyFile" + elif [ "$2" = "WG Slot Is Non-Responsive" ]; then + emailSubject="WARNING: WG Slot $3 is Non-Responsive" + emailBodyTitle="WARNING: WG Slot $3 is Non-Responsive" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "WARNING: VPNMON-R3 has detected that WG Slot $3 is not responding. WG Slot $3 has been reset.\n" + printf "Please check your network environment and configuration if this error continues to persist." + printf "\n" + } > "$tmpEMailBodyFile" + fi + _SendEMailNotification_ "VPNMON-R3 v$version" "$emailSubject" "$tmpEMailBodyFile" "$emailBodyTitle" + fi + + if [ "$1" = "0" ] && [ "$amtmemailsuccess" = "1" ]; then + if [ "$2" = "VPN Connection Scheduled Reset" ]; then + emailSubject="SUCCESS: VPN Slot $3 Manual/Scheduled Reset" + emailBodyTitle="SUCCESS: VPN Slot $3 Manual/Scheduled Reset" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "SUCCESS: VPNMON-R3 completed a successful manual/scheduled reset on VPN Slot $3\n" + printf "\n" + } > "$tmpEMailBodyFile" + elif [ "$2" = "VPN Reset" ]; then + emailSubject="SUCCESS: VPN Slot $3 Manual Reset" + emailBodyTitle="SUCCESS: VPN Slot $3 Manual Reset" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "SUCCESS: VPNMON-R3 completed a successful manual reset on VPN Slot $3\n" + printf "\n" + } > "$tmpEMailBodyFile" + elif [ "$2" = "VPN Killed" ]; then + emailSubject="SUCCESS: VPN Slot $3 Manually Stopped & Unmonitored" + emailBodyTitle="SUCCESS: VPN Slot $3 Manually Stopped & Unmonitored" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "SUCCESS: VPNMON-R3 successfully manually stopped and unmonitored VPN Slot $3\n" + printf "\n" + } > "$tmpEMailBodyFile" + ##-------------------------------------## + ## Added by Dan G. [2025-Jul-16] ## + ##-------------------------------------## + elif [ "$2" = "WG Connection Scheduled Reset" ]; then + emailSubject="SUCCESS: WG Slot $3 Manual/Scheduled Reset" + emailBodyTitle="SUCCESS: WG Slot $3 Manual/Scheduled Reset" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "SUCCESS: VPNMON-R3 completed a successful manual/scheduled reset on WG Slot $3\n" + printf "\n" + } > "$tmpEMailBodyFile" + elif [ "$2" = "WG Reset" ]; then + emailSubject="SUCCESS: WG Slot $3 Manual Reset" + emailBodyTitle="SUCCESS: WG Slot $3 Manual Reset" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "SUCCESS: VPNMON-R3 completed a successful manual reset on WG Slot $3\n" + printf "\n" + } > "$tmpEMailBodyFile" + elif [ "$2" = "WG Killed" ]; then + emailSubject="SUCCESS: WG Slot $3 Manually Stopped & Unmonitored" + emailBodyTitle="SUCCESS: WG Slot $3 Manually Stopped & Unmonitored" + { + printf "Date/Time: $(date +'%b %d %Y %X')\n" + printf "Asus Router Model: ${ROUTERMODEL}\n" + printf "Firmware/Build Number: ${FWBUILD}\n" + printf "\n" + printf "SUCCESS: VPNMON-R3 successfully manually stopped and unmonitored WG Slot $3\n" + printf "\n" + } > "$tmpEMailBodyFile" + fi + _SendEMailNotification_ "VPNMON-R3 v$version" "$emailSubject" "$tmpEMailBodyFile" "$emailBodyTitle" + fi + fi +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Function to keep track of emails sent, and determine if they need to be rate-limited +ratelimiter() +{ + +#if rate limiting is disabled, exit right away +if [ "$ratelimit" = "0" ]; then + return 0 +fi + +#Make sure log file exists +touch "$vr3emails" + +#check current time and 1h into the past +current_time=$(date +%s) +cutoff_time=$((current_time - 3600)) + +#create a temp file where current data will get moved over into that is less than 1hr old +vr3emailstemp="${vr3emails}.tmp" +awk -v cutoff="$cutoff_time" '$1 > cutoff' "$vr3emails" > "$vr3emailstemp" + +#check to see how many emails have been sent in the last hour +recent_email_count=$(wc -l < "$vr3emailstemp" | tr -d ' ') + +printf "\33[2K\r" +printf "${CGreen}\r[Checking email rate limit... $recent_email_count/$ratelimit emails sent within the last hour]" +sleep 2 + +#logic to determine if rate limit has been hit +if [ "$recent_email_count" -ge "$ratelimit" ] + then + printf "\33[2K\r" + printf "${CGreen}\r[Rate limit exceeded. Emails will be prevented from sending]" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Email Rate limit exceeded ($ratelimit). Emails will be prevented from sending." >> $logfile + sleep 2 + mv "$vr3emailstemp" "$vr3emails" + return 1 + else + printf "\33[2K\r" + printf "${CGreen}\r[Rate within limits. Proceeding to send email]" + sleep 1 + echo "$current_time" >> "$vr3emailstemp" + mv "$vr3emailstemp" "$vr3emails" + return 0 +fi + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Testing to see if VPNMON-R3 external reset is currently running, and if so, hold off until it finishes +lockcheck() +{ + + while [ -f "$lockfile" ]; do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} External VPN Reset Currently In-Progress ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} VPNMON-R3 is currently performing an external scheduled reset of the VPN through${CClear}" + echo -e "${InvGreen} ${CClear} the means of the '-reset' commandline option, or scheduled CRON job.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} [Retrying to resume normal operations every 15 seconds]${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo "" + spinner 15 + lockactive=1 + done + + if [ "$lockactive" = "1" ]; then + timerreset=1 + lockactive=0 + fi + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Initiate a VPN restart - $1 = slot number + +restartvpn() +{ + + echo -e "${CGreen}\nMessages: ${CClear}" + echo "" + + #Check the current connection state of vpn slot + currvpnstate="$(_VPN_GetClientState_ "$1")" + + if [ "$currvpnstate" -ne 0 ]; then + printf "${CGreen}\r[Stopping VPN Client $1]" + service stop_vpnclient$1 >/dev/null 2>&1 + sleep 20 + if [ "$currvpnstate" = "-1" ]; then + nvram set vpn_client$1_state=0 + fi + printf "\33[2K\r" + fi + + #Determine how many server entries are in the assigned vpn slot alternate servers file + if [ -f "/jffs/addons/vpnmon-r3.d/vr3svr$1.txt" ] + then + servers=$(cat "/jffs/addons/vpnmon-r3.d/vr3svr$1.txt" | wc -l) >/dev/null 2>&1 + if [ -z "$servers" ] || [ "$servers" -eq 0 ] + then + #Restart the same server currently allocated to that vpn slot + currvpnhost=$($timeoutcmd$timeoutsec nvram get vpn_client$1_addr) + printf "\33[2K\r" + printf "${CGreen}\r[Starting VPN Client $1]" + service start_vpnclient$1 >/dev/null 2>&1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN$1 Connection Restarted - Current Server: $currvpnhost" >> $logfile + resettimer $1 "VPN" + sleep 10 + printf "\33[2K\r" + printf "${CGreen}\r[Letting VPN$1 Settle]" + sleep 10 + else + #Pick a random server from the alternate servers file, populate in vpn client slot, and restart + printf "\33[2K\r" + printf "${CGreen}\r[Selecting Random Entry]" + RANDOM=$(awk 'BEGIN {srand(); print int(32768 * rand())}') + R_LINE=$(( RANDOM % servers + 1 )) + RNDVPNIP=$(sed -n "${R_LINE}p" /jffs/addons/vpnmon-r3.d/vr3svr$1.txt) + nvram set vpn_client"$1"_addr="$RNDVPNIP" + nvram set vpn_client"$1"_desc="VPN$1 - $RNDVPNIP added by VPNMON-R3" + sleep 2 + #Restart the new server currently allocated to that vpn slot + printf "\33[2K\r" + printf "${CGreen}\r[Starting VPN Client $1]" + service start_vpnclient$1 >/dev/null 2>&1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN$1 Connection Restarted - New Server: $RNDVPNIP" >> $logfile + resettimer $1 "VPN" + sleep 10 + printf "\33[2K\r" + printf "${CGreen}\r[Letting VPN$1 Settle]" + sleep 10 + fi + else + #Restart the same server currently allocated to that vpn slot + printf "\33[2K\r" + printf "${CGreen}\r[Starting VPN Client $1]" + currvpnhost="$($timeoutcmd$timeoutsec nvram get vpn_client$1_addr)" + service start_vpnclient$1 >/dev/null 2>&1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN$1 Connection Restarted - Current Server: $currvpnhost" >> $logfile + resettimer $1 "VPN" + sleep 10 + printf "\33[2K\r" + printf "${CGreen}\r[Letting VPN$1 Settle]" + sleep 10 + fi + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Initiate a WG restart - $1 = slot number + +restartwg() +{ + + echo -e "${CGreen}\nMessages: ${CClear}" + echo "" + + #Check the current connection state of vpn slot + currwgstate="$(_WG_GetClientState_ $1)" + + if [ "$currwgstate" -ne 0 ]; then + printf "${CGreen}\r[Stopping WG Client $1]" + service "stop_wgc $1" >/dev/null 2>&1 + sleep 20 + printf "\33[2K\r" + fi + + #Determine how many server entries are in the assigned vpn slot alternate servers file + if [ -f "/jffs/addons/vpnmon-r3.d/vr3wgsvr$1.txt" ] + then + servers=$(cat "/jffs/addons/vpnmon-r3.d/vr3wgsvr$1.txt" | wc -l) >/dev/null 2>&1 + if [ -z "$servers" ] || [ "$servers" -eq 0 ] + then + #Restart the same server currently allocated to that wg slot + currwghost="$($timeoutcmd$timeoutsec nvram get wgc$1_ep_addr)" + currwghostname="$($timeoutcmd$timeoutsec nvram get wgc$1_desc)" + printf "\33[2K\r" + printf "${CGreen}\r[Starting WG Client $1]" + service "start_wgc $1" >/dev/null 2>&1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WG$1 Connection Restarted - Current Server: $currwghostname | $currwghost" >> $logfile + resettimer $1 "WG" + sleep 10 + printf "\33[2K\r" + printf "${CGreen}\r[Letting WG$1 Settle]" + sleep 10 + else + #Pick a random server from the alternate servers file, populate in wg client slot, and restart + printf "\33[2K\r" + printf "${CGreen}\r[Selecting Random Entry]" + RANDOM=$(awk 'BEGIN {srand(); print int(32768 * rand())}') + R_LINE=$(( RANDOM % servers + 1 )) + + #---------WG-specific nvram values + + WGLINE=$(sed -n "${R_LINE}p" /jffs/addons/vpnmon-r3.d/vr3wgsvr$1.txt) + wgdescription=$(echo "$WGLINE" | cut -d ',' -f 1) + endpointip=$(echo "$WGLINE" | cut -d ',' -f 2) + endpointport=$(echo "$WGLINE" | cut -d ',' -f 3) + privatekey=$(echo "$WGLINE" | cut -d ',' -f 4) + publickey=$(echo "$WGLINE" | cut -d ',' -f 5) + nvram set wgc"$1"_desc="$wgdescription" + nvram set wgc"$1"_ep_addr="$endpointip" + nvram set wgc"$1"_ep_addr_r="$endpointip" + nvram set wgc"$1"_ep_port="$endpointport" + nvram set wgc"$1"_priv="${privatekey}" + nvram set wgc"$1"_ppub="${publickey}" + sleep 2 + + #---------WG-specific nvram values + + #Restart the new server currently allocated to that wg slot + printf "\33[2K\r" + printf "${CGreen}\r[Starting WG Client $1]" + service "start_wgc $1" >/dev/null 2>&1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WG$1 Connection Restarted - New Server: $wgdescription | $endpointip" >> $logfile + resettimer $1 "WG" + sleep 10 + printf "\33[2K\r" + printf "${CGreen}\r[Letting WG$1 Settle]" + sleep 10 + fi + else + #Restart the same server currently allocated to that wg slot + printf "\33[2K\r" + printf "${CGreen}\r[Starting WG Client $1]" + currwghost="$($timeoutcmd$timeoutsec nvram get wgc$1_ep_addr)" + service "start_wgc $1" >/dev/null 2>&1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WG$1 Connection Restarted - Current Server: $currwghost" >> $logfile + resettimer $1 "WG" + sleep 10 + printf "\33[2K\r" + printf "${CGreen}\r[Letting WG$1 Settle]" + sleep 10 + fi + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Separating off the vpnrouting service restart + +restartrouting() +{ + + printf "\33[2K\r" + printf "${CGreen}\r[Restarting VPN Routing]" + service restart_vpnrouting0 >/dev/null 2>&1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN Director Routing Service Restarted" >> $logfile + sleep 5 + printf "\33[2K\r" + trimlogs + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Optionally reset spdMerlin interfaces +resetspdmerlin() +{ + + if [ "$rstspdmerlin" = "1" ] + then + printf "\33[2K\r" + printf "${CGreen}\r[Reset spdMerlin Interfaces]" + /jffs/scripts/spdmerlin reset_interfaces force >/dev/null 2>&1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: spdMerlin Interfaces Reset" >> $logfile + sleep 5 + printf "\33[2K\r" + fi + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Kill a vpn connnection, and unmonitor it - $1 = slot number + +killunmonvpn() +{ + + # Stop the service + echo -e "${CGreen}\nMessages: ${CClear}" + echo "" + printf "${CGreen}\r[Stopping VPN Client $1]" + service stop_vpnclient$1 >/dev/null 2>&1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN$1 has been stopped and no longer being monitored" >> $logfile + sleep 15 + printf "\33[2K\r" + printf "${CGreen}\r[Unmonitoring VPN Client $1]" + + # Write the VPN client file back with the correct monitoring configuration + sed -i "s/^VPN$1=.*/VPN$1=0/" "/jffs/addons/vpnmon-r3.d/vr3clients.txt" + sleep 5 + + # Restart VPN Director Routing Services + restartrouting + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Kill a wg connnection, and unmonitor it - $1 = slot number + +killunmonwg() +{ + + # Stop the service + echo -e "${CGreen}\nMessages: ${CClear}" + echo "" + printf "${CGreen}\r[Stopping WG Client $1]" + service "stop_wgc $1" >/dev/null 2>&1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WG$1 has been stopped and no longer being monitored" >> $logfile + sleep 15 + printf "\33[2K\r" + printf "${CGreen}\r[Unmonitoring WG Client $1]" + + # Write the VPN client file back with the correct monitoring configuration + sed -i "s/^WG$1=.*/WG$1=0/" "/jffs/addons/vpnmon-r3.d/vr3clients.txt" + sleep 5 + + # Restart VPN Director Routing Services + restartrouting + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Whitelist Server Slot IP lists in Skynet - $1 = VPN Slot + +skynetwhitelist() +{ + if [ "$updateskynet" = "1" ] + then + ##-------------------------------------## + ## Modified by Dan G. [2025-Jul-15] ## + ##-------------------------------------## + if echo "$1" | grep -q "wg"; then + slotnum=$(echo "$1" | tr -cd '0-9') + printf "${CGreen}\r[Whitelisting WG Server Slot $slotnum List in the Skynet Firewall]${CClear}\n" + awk -F',' '{print $2}' /jffs/addons/vpnmon-r3.d/vr3wgsvr${slotnum}.txt > /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt + firewall import whitelist /jffs/addons/vpnmon-r3.d/vr3svrtmp.txt "VPNMON-R3 - WG Server Slot $slotnum Whitelist" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WG Server Slot $slotnum List has been whitelisted in Skynet" >> $logfile + else + printf "${CGreen}\r[Whitelisting VPN Server Slot $1 List in the Skynet Firewall]${CClear}\n" + firewall import whitelist /jffs/addons/vpnmon-r3.d/vr3svr$1.txt "VPNMON-R3 - VPN Server Slot $1 Whitelist" >/dev/null 2>&1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN Server Slot $1 List has been whitelisted in Skynet" >> $logfile + fi + sleep 5 + fi +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Reset the VPN connection timer + +resettimer() +{ + + # Create initial vr3timers.txt file if it does not exist + if [ ! -f /jffs/addons/vpnmon-r3.d/vr3timers.txt ]; then + + if [ "$availableslots" = "1 2" ]; then + { echo 'VPNTIMER1=0' + echo 'VPNTIMER2=0' + } > /jffs/addons/vpnmon-r3.d/vr3timers.txt + + elif [ "$availableslots" = "1 2 3 4 5" ]; then + { echo 'VPNTIMER1=0' + echo 'VPNTIMER2=0' + echo 'VPNTIMER3=0' + echo 'VPNTIMER4=0' + echo 'VPNTIMER5=0' + echo 'WGTIMER1=0' + echo 'WGTIMER2=0' + echo 'WGTIMER3=0' + echo 'WGTIMER4=0' + echo 'WGTIMER5=0' + } > /jffs/addons/vpnmon-r3.d/vr3timers.txt + + fi + fi + + if [ "$2" = "VPN" ]; then + sed -i "s/^VPNTIMER$1=.*/VPNTIMER$1=$(date +%s)/" "/jffs/addons/vpnmon-r3.d/vr3timers.txt" + elif [ "$2" = "WG" ]; then + sed -i "s/^WGTIMER$1=.*/WGTIMER$1=$(date +%s)/" "/jffs/addons/vpnmon-r3.d/vr3timers.txt" + fi + + source /jffs/addons/vpnmon-r3.d/vr3timers.txt + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Reset the managed VPN connections + +vreset() +{ + # Grab the VPNMON-R3 config file and read it in + if [ -f "$config" ] + then + source "$config" + else + clear + echo -e "${CRed}ERROR: VPNMON-R3 is not configured. Please run 'vpnmon-r3.sh -setup' first." + echo "" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: VPNMON-R3 config file not found. Please run the setup/configuration utility" >> $logfile + echo -e "${CClear}" + exit 1 + fi + + # Grab the monitored slots file and read it in + if [ -f /jffs/addons/vpnmon-r3.d/vr3clients.txt ] + then + source /jffs/addons/vpnmon-r3.d/vr3clients.txt + else + clear + echo -e "${CRed}ERROR: VPNMON-R3 is not configured. Please run 'vpnmon-r3.sh -setup' first." + echo "" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: VPNMON-R3 VPN Client Monitoring file not found. Please run the setup/configuration utility" >> $logfile + echo -e "${CClear}" + exit 1 + fi + + # Create a rudimentary lockfile so that VPNMON-R3 doesn't interfere during the reset + echo -n > $lockfile + + slot=0 + for slot in $availableslots #loop through the 2/5 vpn slots + do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} VPNMON-R3 - v$version | $(date) ${CClear}\n" + echo -e "${CGreen}[VPN Connection Reset Commencing]" + echo "" + echo -e "${CGreen}[Checking VPN Slot $slot]" + echo "" + sleep 2 + + #determine if the slot is monitored and reset it + if [ "$((VPN$slot))" = "1" ] + then + if [ "$refreshserverlists" -eq 1 ] + then + if [ -f "/jffs/addons/vpnmon-r3.d/vr3svr$slot.txt" ] + then + echo -e "${CGreen}[Executing Custom Server List Script for VPN Slot $slot]${CClear}" + slottmp="automation${slot}" + eval slottmp="\$${slottmp}" + if [ -z "$slottmp" ] + then + echo "" + echo -e "${CGreen}[Custom VPN Client Server Query not found for VPN Slot $slot]${CClear}" + else + automationunenc="$(echo "$slottmp" | openssl enc -d -base64 -A)" + echo "" + echo -e "${CClear}Running: $automationunenc" + echo "" + + eval "$automationunenc" > "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" + if [ -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" ] + then + dlcnt=$(cat "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" | wc -l) >/dev/null 2>&1 + if [ -z "$dlcnt" ] || [ "$dlcnt" -lt 1 ] + then dlcnt=0 ; fi + else + dlcnt=0 + fi + + if [ "$dlcnt" -gt 1 ] + then + cp "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" "/jffs/addons/vpnmon-r3.d/vr3svr$slot.txt" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom VPN Client Server List Query Executed for VPN Slot $slot ($dlcnt rows)" >> $logfile + sleep 3 + echo "" + skynetwhitelist $slot + echo "" + else + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source - Preserving Original Server List]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom VPN Client Server List Query for VPN Slot $slot yielded 0 rows -- Query may be invalid or VPN API service may be down" >> $logfile + sendmessage 1 "VPN Server List Query Yielded 0 Rows" $slot + sleep 3 + echo "" + fi + fi + else + echo "" + echo -e "${CRed}[Custom VPN Client Server List File not found for VPN Slot $slot]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom VPN Client Server List File not found for VPN Slot $slot" >> $logfile + sleep 3 + fi + fi + + restartvpn $slot + sendmessage 0 "VPN Connection Scheduled Reset" $slot + + fi + done + + slot=0 + for slot in $availableslots #loop through the 5 wg slots + do + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} VPNMON-R3 - v$version | $(date) ${CClear}\n" + echo -e "${CGreen}[WG Connection Reset Commencing]" + echo "" + echo -e "${CGreen}[Checking WG Slot $slot]" + echo "" + sleep 2 + + #determine if the slot is monitored and reset it + if [ "$((WG$slot))" = "1" ] + then + if [ "$refreshserverlists" -eq 1 ] + then + if [ -f "/jffs/addons/vpnmon-r3.d/vr3wgsvr$slot.txt" ] + then + echo -e "${CGreen}[Executing Custom WG Server List Script for WG Slot $slot]${CClear}" + slottmp="wgautomation${slot}" + eval slottmp="\$${slottmp}" + if [ -z "$slottmp" ] + then + echo "" + echo -e "${CGreen}[Custom WG Client Server Query not found for WG Slot $slot]${CClear}" + else + wgautomationunenc="$(echo "$slottmp" | openssl enc -d -base64 -A)" + echo "" + echo -e "${CClear}Running: $wgautomationunenc" + echo "" + + eval "$wgautomationunenc" > "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" + if [ -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" ] + then + dlcnt=$(cat "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" | wc -l) >/dev/null 2>&1 + if [ -z "$dlcnt" ] || [ "$dlcnt" -lt 1 ] + then dlcnt=0 ; fi + else + dlcnt=0 + fi + + if [ "$dlcnt" -gt 1 ] + then + cp "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" "/jffs/addons/vpnmon-r3.d/vr3wgsvr$slot.txt" >/dev/null 2>&1 + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Custom WG Client Server List Query Executed for WG Slot $slot ($dlcnt rows)" >> $logfile + sleep 3 + echo "" + skynetwhitelist wg$slot + echo "" + else + rm -f "/jffs/addons/vpnmon-r3.d/vr3svrtmp.txt" >/dev/null 2>&1 + echo -e "${CGreen}[$dlcnt Rows Retrieved From Source - Preserving Original Server List]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom WG Client Server List Query for WG Slot $slot yielded 0 rows -- Query may be invalid or WG API service may be down" >> $logfile + sendmessage 1 "WG Server List Query Yielded 0 Rows" $slot + sleep 3 + echo "" + fi + fi + else + echo "" + echo -e "${CRed}[Custom WG Client Server List File not found for WG Slot $slot]${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Custom WG Client Server List File not found for WG Slot $slot" >> $logfile + sleep 3 + fi + fi + restartwg $slot + sendmessage 0 "WG Connection Scheduled Reset" $slot + + fi + done + + restartrouting + resetspdmerlin + + # Clean up lockfile + rm -f $lockfile >/dev/null 2>&1 + + echo -e "\n${CClear}" + exit 0 +} + +# ------------------------------------------------------------------------------------------------------------------------- +##----------------------------------------## +## Modified by Martinski W. [2024-Oct-18] ## +##----------------------------------------## +# Find the VPN IP +getvpnip() +{ + ubsync="" + TUN="tun1$1" + + icanhazvpnip="$($timeoutcmd$timeoutsec nvram get vpn_client$1_rip)" + if [ -z "$icanhazvpnip" ] || [ "$icanhazvpnip" = "unknown" ] + then + # Grab the public IP of the VPN Connection # + icanhazvpnip="curl --silent --retry 3 --retry-delay 2 --retry-all-errors --fail --interface "$TUN" --request GET --url https://ipv4.icanhazip.com" + icanhazvpnip="$(eval $icanhazvpnip)" + if [ -z "$icanhazvpnip" ] || echo "$icanhazvpnip" | grep -qoE 'Internet|traffic|Error|error' ; then icanhazvpnip="0.0.0.0" ; fi + fi + + if [ -z "$icanhazvpnip" ] + then + vpnip="000.000.000.000" + return + else + vpnip="$(printf '%15s' "$icanhazvpnip")" + fi + + if [ "$unboundclient" -ne 0 ] && [ "$unboundclient" -eq "$1" ] + then + if [ "$ResolverTimer" -eq 1 ]; then + ResolverTimer=0 + if [ "$unboundshowip" -eq 0 ]; then + ubsync="${CYellow}-?[UB]${CClear}" + else + ubsync="${CYellow}-?[UB:Resolving]${CClear}" + fi + else + # Huge thanks to @SomewhereOverTheRainbow for his expertise in troublshooting and coming up with this DNS Resolver methodology! + DNSResolver="$({ unbound-control flush whoami.akamai.net >/dev/null 2>&1; } && dig whoami.akamai.net +short @"$(netstat -nlp 2>/dev/null | awk '/.*(unbound){1}.*/{split($4, ip_addr, ":");if(substr($4,11) !~ /.*953.*/)print ip_addr[1];if(substr($4,11) !~ /.*953.*/)exit}')" -p "$(netstat -nlp 2>/dev/null | awk '/.*(unbound){1}.*/{if(substr($4,11) !~ /.*953.*/)print substr($4,11);if(substr($4,11) !~ /.*953.*/)exit}')" 2>/dev/null)" + + if [ -z "$DNSResolver" ] + then + if [ "$unboundshowip" -eq 0 ]; then + ubsync="${CRed}-X[UB]${CClear}" + else + ubsync="${CRed}-X[UB:$DNSResolver]${CClear}" + fi + # rudimentary check to make sure value coming back is in the format of an IP address... Don't care if it's more than 255. + elif expr "$DNSResolver" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null + then + # If the DNS resolver and public VPN IP address don't match in our Unbound scenario, reset! + if [ "$DNSResolver" != "$icanhazvpnip" ] + then + if [ "$unboundshowip" -eq 0 ]; then + ubsync="${CRed}-X[UB]${CClear}" + else + ubsync="${CRed}-X[UB:$DNSResolver]${CClear}" + fi + ResolverTimer=1 + unboundreset=$1 + else + if [ "$unboundshowip" -eq 0 ]; then + ubsync="${CGreen}->[UB]${CClear}" + else + ubsync="${CGreen}->[UB:$DNSResolver]${CClear}" + fi + fi + else + if [ "$unboundshowip" -eq 0 ]; then + ubsync="${CYellow}-?[UB]${CClear}" + else + ubsync="${CYellow}-?[UB:$DNSResolver]${CClear}" + fi + fi + fi + else + ubsync="" + fi + + # Insert bogus IP if screenshotmode is on # + if [ "$screenshotmode" = "1" ]; then + vpnip="$(printf '%15s' "12.34.56.78")" + fi +} + +# ------------------------------------------------------------------------------------------------------------------------- +##-----------------------------------------------------------------------------------------## +## Modified by ViktorJp [2025-Jul-06], origial getvpnip modded by Martinski. [2024-Oct-18] ## +##-----------------------------------------------------------------------------------------## +# Find the WG IP +getwgip() +{ + TUN="wgc$1" + + # Added ping workaround for site2site scenarios based on suggestion from @ZebMcKayhan + TUN_IP=$($timeoutcmd$timeoutsec nvram get "$TUN"_addr | cut -d '/' -f1) + ip rule add from $TUN_IP lookup $TUN prio 10 >/dev/null 2>&1 + + icanhazwgip="curl --silent --retry 3 --retry-delay 2 --retry-all-errors --fail --interface "$TUN" --request GET --url https://ipv4.icanhazip.com" + icanhazwgip="$(eval $icanhazwgip)" + if [ -z "$icanhazwgip" ] || echo "$icanhazwgip" | grep -qoE 'Internet|traffic|Error|error' ; then icanhazwgip="0.0.0.0" ; fi + + # Added based on suggestion from @ZebMcKayhan + ip rule del prio 10 >/dev/null 2>&1 + + if [ -z "$icanhazwgip" ] + then + wgip="000.000.000.000" + return + else + wgip="$(printf '%15s' "$icanhazwgip")" + fi + + # Insert bogus IP if screenshotmode is on # + if [ "$screenshotmode" = "1" ]; then + wgip="$(printf '%15s' "12.34.56.78")" + fi +} + +# ------------------------------------------------------------------------------------------------------------------------- +##----------------------------------------## +## Modified by Martinski W. [2024-Oct-18] ## +##----------------------------------------## +# Find and remember the VPN city so it doesn't have to make successive API lookups +getvpncity() +{ + if [ "$icanhazvpnip" = "0.0.0.0" ]; then + vpncity="Undetermined" + return + fi + + if [ "$1" = "1" ] + then + lastvpnip1="$icanhazvpnip" + if [ "$lastvpnip1" != "$oldvpnip1" ] + then + vpncity="curl --silent --retry 3 --retry-delay 2 --retry-all-errors --request GET --url http://ip-api.com/json/$icanhazvpnip | jq --raw-output .city" + vpncity="$(eval $vpncity)" + if [ -z "$vpncity" ] || echo "$vpncity" | grep -qoE '\b(error.*:.*True.*|Undefined)\b' ; then vpncity="Undetermined" ; fi + citychange="${CGreen}- NEW ${CClear}" + vpncity1="$vpncity" + fi + vpncity="$vpncity1" + oldvpnip1="$lastvpnip1" + elif [ "$1" = "2" ] + then + lastvpnip2="$icanhazvpnip" + if [ "$lastvpnip2" != "$oldvpnip2" ] + then + vpncity="curl --silent --retry 3 --retry-delay 2 --retry-all-errors --request GET --url http://ip-api.com/json/$icanhazvpnip | jq --raw-output .city" + vpncity="$(eval $vpncity)" + if [ -z "$vpncity" ] || echo "$vpncity" | grep -qoE '\b(error.*:.*True.*|Undefined)\b' ; then vpncity="Undetermined" ; fi + citychange="${CGreen}- NEW ${CClear}" + vpncity2="$vpncity" + fi + vpncity="$vpncity2" + oldvpnip2="$lastvpnip2" + elif [ "$1" = "3" ] + then + lastvpnip3="$icanhazvpnip" + if [ "$lastvpnip3" != "$oldvpnip3" ] + then + vpncity="curl --silent --retry 3 --retry-delay 2 --retry-all-errors --request GET --url http://ip-api.com/json/$icanhazvpnip | jq --raw-output .city" + vpncity="$(eval $vpncity)" + if [ -z "$vpncity" ] || echo "$vpncity" | grep -qoE '\b(error.*:.*True.*|Undefined)\b' ; then vpncity="Undetermined" ; fi + citychange="${CGreen}- NEW ${CClear}" + vpncity3="$vpncity" + fi + vpncity="$vpncity3" + oldvpnip3="$lastvpnip3" + elif [ "$1" = "4" ] + then + lastvpnip4="$icanhazvpnip" + if [ "$lastvpnip4" != "$oldvpnip4" ] + then + vpncity="curl --silent --retry 3 --retry-delay 2 --retry-all-errors --request GET --url http://ip-api.com/json/$icanhazvpnip | jq --raw-output .city" + vpncity="$(eval $vpncity)" + if [ -z "$vpncity" ] || echo "$vpncity" | grep -qoE '\b(error.*:.*True.*|Undefined)\b' ; then vpncity="Undetermined" ; fi + citychange="${CGreen}- NEW ${CClear}" + vpncity4="$vpncity" + fi + vpncity="$vpncity4" + oldvpnip4="$lastvpnip4" + elif [ "$1" = "5" ] + then + lastvpnip5="$icanhazvpnip" + if [ "$lastvpnip5" != "$oldvpnip5" ] + then + vpncity="curl --silent --retry 3 --retry-delay 2 --retry-all-errors --request GET --url http://ip-api.com/json/$icanhazvpnip | jq --raw-output .city" + vpncity="$(eval $vpncity)" + if [ -z "$vpncity" ] || echo "$vpncity" | grep -qoE '\b(error.*:.*True.*|Undefined)\b' ; then vpncity="Undetermined" ; fi + citychange="${CGreen}- NEW ${CClear}" + vpncity5="$vpncity" + fi + vpncity="$vpncity5" + oldvpnip5="$lastvpnip5" + fi + + # Insert bogus City if screenshotmode is on + if [ "$screenshotmode" = "1" ]; then + vpncity="Gotham City" + fi +} + +# ------------------------------------------------------------------------------------------------------------------------- +##------------------------------------------------------------------------------------------------## +## Modified by ViktorJp [2025-Jul-06], Original getvpncity() modded bt Martinski W. [2024-Oct-18] ## +##------------------------------------------------------------------------------------------------## +# Find and remember the WG city so it doesn't have to make successive API lookups +getwgcity() +{ + if [ "$icanhazwgip" = "0.0.0.0" ]; then + wgcity="Undetermined" + return + fi + + # Added ping workaround for site2site scenarios based on suggestion from @ZebMcKayhan + TUN_IP=$($timeoutcmd$timeoutsec nvram get "$TUN"_addr | cut -d '/' -f1) + ip rule add from $TUN_IP lookup $TUN prio 10 >/dev/null 2>&1 + + if [ "$1" = "1" ] + then + lastwgip1="$icanhazwgip" + if [ "$lastwgip1" != "$oldwgip1" ] + then + wgcity="curl --silent --retry 3 --retry-delay 2 --retry-all-errors --request GET --url http://ip-api.com/json/$icanhazwgip | jq --raw-output .city" + wgcity="$(eval $wgcity)" + if [ -z "$wgcity" ] || echo "$wgcity" | grep -qoE '\b(error.*:.*True.*|Undefined)\b' ; then wgcity="Undetermined" ; fi + wgcitychange="${CGreen}- NEW ${CClear}" + wgcity1="$wgcity" + fi + wgcity="$wgcity1" + oldwgip1="$lastwgip1" + elif [ "$1" = "2" ] + then + lastwgip2="$icanhazwgip" + if [ "$lastwgip2" != "$oldwgip2" ] + then + wgcity="curl --silent --retry 3 --retry-delay 2 --retry-all-errors --request GET --url http://ip-api.com/json/$icanhazwgip | jq --raw-output .city" + wgcity="$(eval $wgcity)" + if [ -z "$wgcity" ] || echo "$wgcity" | grep -qoE '\b(error.*:.*True.*|Undefined)\b' ; then wgcity="Undetermined" ; fi + wgcitychange="${CGreen}- NEW ${CClear}" + wgcity2="$wgcity" + fi + wgcity="$wgcity2" + oldwgip2="$lastwgip2" + elif [ "$1" = "3" ] + then + lastwgip3="$icanhazwgip" + if [ "$lastwgip3" != "$oldwgip3" ] + then + wgcity="curl --silent --retry 3 --retry-delay 2 --retry-all-errors --request GET --url http://ip-api.com/json/$icanhazwgip | jq --raw-output .city" + wgcity="$(eval $wgcity)" + if [ -z "$wgcity" ] || echo "$wgcity" | grep -qoE '\b(error.*:.*True.*|Undefined)\b' ; then wgcity="Undetermined" ; fi + wgcitychange="${CGreen}- NEW ${CClear}" + wgcity3="$wgcity" + fi + wgcity="$wgcity3" + oldwgip3="$lastwgip3" + elif [ "$1" = "4" ] + then + lastwgip4="$icanhazwgip" + if [ "$lastwgip4" != "$oldwgip4" ] + then + wgcity="curl --silent --retry 3 --retry-delay 2 --retry-all-errors --request GET --url http://ip-api.com/json/$icanhazwgip | jq --raw-output .city" + wgcity="$(eval $wgcity)" + if [ -z "$wgcity" ] || echo "$wgcity" | grep -qoE '\b(error.*:.*True.*|Undefined)\b' ; then wgcity="Undetermined" ; fi + wgcitychange="${CGreen}- NEW ${CClear}" + wgcity4="$wgcity" + fi + wgcity="$wgcity4" + oldwgip4="$lastwgip4" + elif [ "$1" = "5" ] + then + lastwgip5="$icanhazwgip" + if [ "$lastwgip5" != "$oldwgip5" ] + then + wgcity="curl --silent --retry 3 --retry-delay 2 --retry-all-errors --request GET --url http://ip-api.com/json/$icanhazwgip | jq --raw-output .city" + wgcity="$(eval $wgcity)" + if [ -z "$wgcity" ] || echo "$wgcity" | grep -qoE '\b(error.*:.*True.*|Undefined)\b' ; then wgcity="Undetermined" ; fi + wgcitychange="${CGreen}- NEW ${CClear}" + wgcity5="$wgcity" + fi + wgcity="$wgcity5" + oldwgip5="$lastwgip5" + fi + + # Added based on suggestion from @ZebMcKayhan + ip rule del prio 10 >/dev/null 2>&1 + + # Insert bogus City if screenshotmode is on + if [ "$screenshotmode" = "1" ]; then + wgcity="Gotham City" + fi +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Check health of the vpn connection using PING and CURL + +checkvpn() +{ + CNT=0 + TRIES=3 + TUN="tun1$1" + + while [ "$CNT" -lt "$TRIES" ]; do # Loop through number of tries + ping -I $TUN -q -c 1 -W 2 $PINGHOST > /dev/null 2>&1 # First try pings + RC=$? + # Grab the public IP of the VPN Connection # + ICANHAZIP="$(curl --silent --retry 3 --retry-delay 2 --retry-all-errors --fail --interface "$TUN" --request GET --url https://ipv4.icanhazip.com)" + IC=$? + if [ "$RC" -eq 0 ] && [ "$IC" -eq 0 ]; then # If both ping/curl come back successful, then proceed + vpnping=$(ping -I $TUN -c 1 -W 2 $PINGHOST | awk -F'time=| ms' 'NF==3{print $(NF-1)}' | sort -rn) > /dev/null 2>&1 + VP=$? + if [ "$VP" -eq 0 ]; then + vpnhealth="${CGreen}[ OK ]${CClear}" + vpnindicator="${InvGreen} ${CClear}" + else + vpnping=0 + vpnhealth="${CYellow}[UNKN]${CClear}" + vpnindicator="${InvYellow} ${CClear}" + fi + printf "\33[2K\r" + break + else + CNT="$((CNT+1))" + printf "\33[2K\r" + printf "\r${InvDkGray} ${CWhite} VPN$1${CClear} [Attempt $CNT]" + sleep 1 # Giving the VPN a chance to recover a certain number of times + + if [ "$CNT" -eq "$TRIES" ];then # But if it fails, report back that we have an issue requiring a VPN reset + printf "\33[2K\r" + vpnping=0 + vpnhealth="${CRed}[FAIL]${CClear}" + vpnindicator="${InvRed} ${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: VPN$1 failed to respond" >> $logfile + if [ "$((VPN$1))" = "1" ]; then + resetvpn=$1 + fi + fi + fi + done +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Check health of the WG connection using PING and CURL + +checkwg() +{ + CNT=0 + TRIES=3 + TUN="wgc$1" + + # Added ping workaround for site2site scenarios based on suggestion from @ZebMcKayhan + TUN_IP=$($timeoutcmd$timeoutsec nvram get "$TUN"_addr | cut -d '/' -f1) + ip rule add from $TUN_IP lookup $TUN prio 10 >/dev/null 2>&1 + + while [ "$CNT" -lt "$TRIES" ]; do # Loop through number of tries + ping -I $TUN -q -c 1 -W 2 $PINGHOST > /dev/null 2>&1 # First try pings + RC=$? + # Grab the public IP of the VPN Connection # + ICANHAZIP="$(curl --silent --retry 3 --retry-delay 2 --retry-all-errors --fail --interface "$TUN" --request GET --url https://ipv4.icanhazip.com)" + IC=$? + if [ "$RC" -eq 0 ] && [ "$IC" -eq 0 ]; then # If both ping/curl come back successful, then proceed + wgping=$(ping -I $TUN -c 1 -W 2 $PINGHOST | awk -F'time=| ms' 'NF==3{print $(NF-1)}' | sort -rn) > /dev/null 2>&1 + VP=$? + if [ "$VP" -eq 0 ]; then + wghealth="${CGreen}[ OK ]${CClear}" + wgindicator="${InvGreen} ${CClear}" + else + wgping=0 + wghealth="${CYellow}[UNKN]${CClear}" + wgindicator="${InvYellow} ${CClear}" + fi + printf "\33[2K\r" + break + else + CNT="$((CNT+1))" + printf "\33[2K\r" + printf "\r${InvDkGray} ${CWhite} WG$1${CClear} [Attempt $CNT]" + sleep 1 # Giving the VPN a chance to recover a certain number of times + + if [ "$CNT" -eq "$TRIES" ];then # But if it fails, report back that we have an issue requiring a VPN reset + printf "\33[2K\r" + wgping=0 + wghealth="${CRed}[FAIL]${CClear}" + wgindicator="${InvRed} ${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WG$1 failed to respond" >> $logfile + if [ "$((WG$1))" = "1" ]; then + resetwg=$1 + fi + fi + fi + done + + # Added based on suggestion from @ZebMcKayhan + ip rule del prio 10 >/dev/null 2>&1 + +} + +# ------------------------------------------------------------------------------------------------------------------------- + +# Checkwan is a function that checks the viability of the current WAN connection and will loop until the WAN connection is restored. +checkwan() +{ + # Using Google's DNS server as default to test for WAN connectivity over verified SSL Handshake + wandownbreakertrip=0 + testssl="$PING_HOST_Deflt" + + printf "\33[2K\r" + printf "\r${InvYellow} ${CClear} [Checking WAN Connectivity]..." + + #Run main checkwan loop + while true + do + # Check the actual WAN State from NVRAM before running connectivity test, or insert itself into loop after failing an SSL handshake test + if [ "$($timeoutcmd$timeoutsec nvram get wan0_state_t)" -eq 2 ] || [ "$($timeoutcmd$timeoutsec nvram get wan1_state_t)" -eq 2 ] + then + # Test the active WAN connection using 443 and verifying a handshake... if this fails, then the WAN connection is most likely down... or Google is down ;) + if ($timeoutcmd$timeoutlng nc -w3 $testssl 443 >/dev/null 2>&1 && echo | $timeoutcmd$timeoutlng openssl s_client -connect $testssl:443 >/dev/null 2>&1 | awk 'handshake && $1 == "Verification" { if ($2=="OK") exit; exit 1 } $1 $2 == "SSLhandshake" { handshake = 1 }' >/dev/null 2>&1) + ##OFF##if ($timeoutcmd$timeoutlng nc -w1 $testssl 443 >/dev/null 2>&1 && echo | $timeoutcmd$timeoutlng openssl s_client -connect $testssl:443 >/dev/null 2>&1 | awk '$1 == "SSL" && $2 == "handshake" { handshake = 1 } handshake && $1 == "Verification:" { ok = $2; exit } END { exit ok != "OK" }' >/dev/null 2>&1) + then + printf "\r${InvGreen} ${CClear} [Checking WAN Connectivity]...ACTIVE" + sleep 1 + printf "\33[2K\r" + return + else + wandownbreakertrip=1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: WAN Connectivity Issue Detected" >> $logfile + fi + else + wandownbreakertrip=1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: WAN Connectivity Issue Detected" >> $logfile + fi + + if [ "$wandownbreakertrip" = "1" ] + then + # The WAN is most likely down, and keep looping through until NVRAM reports that it's back up + while [ "$wandownbreakertrip" = "1" ] + do + if [ "$availableslots" = "1 2" ] + then + state1="$(_VPN_GetClientState_ 1)" + state2="$(_VPN_GetClientState_ 2)" + printf "\r${InvGreen} ${CClear} [Confirming VPN Clients Disconnected]... 1:$state1 2:$state2 " + sleep 3 + elif [ "$availableslots" = "1 2 3 4 5" ] + then + state1="$(_VPN_GetClientState_ 1)" + state2="$(_VPN_GetClientState_ 2)" + state3="$(_VPN_GetClientState_ 3)" + state4="$(_VPN_GetClientState_ 4)" + state5="$(_VPN_GetClientState_ 5)" + printf "\r${InvGreen} ${CClear} [Confirming VPN Clients Disconnected]... 1:$state1 2:$state2 3:$state3 4:$state4 5:$state5 " + sleep 3 + fi + + # Preemptively kill all the VPN Clients incase they're trying to reconnect on their own + for slot in $availableslots + do + if [ $((state$slot)) -ne 0 ]; then + printf "\r${InvGreen} ${CClear} [Retrying Kill Command on VPN$slot Client Connection]... " + service stop_vpnclient$slot >/dev/null 2>&1 + sleep 3 + fi + done + + # Check the wireguard side and bring those down as well + if [ "$availableslots" = "1 2 3 4 5" ] + then + wgstate1="$(_WG_GetClientState_ 1)" + wgstate2="$(_WG_GetClientState_ 2)" + wgstate3="$(_WG_GetClientState_ 3)" + wgstate4="$(_WG_GetClientState_ 4)" + wgstate5="$(_WG_GetClientState_ 5)" + printf "\r${InvGreen} ${CClear} [Confirming WG Clients Disconnected]... 1:$wgstate1 2:$wgstate2 3:$wgstate3 4:$wgstate4 5:$wgstate5 " + sleep 3 + fi + + # Preemptively kill all the WG Clients incase they're trying to reconnect on their own + for slot in $availableslots + do + if [ $((wgstate$slot)) -ne 0 ]; then + printf "\r${InvGreen} ${CClear} [Retrying Kill Command on WG$slot Client Connection]... " + service "stop_wgc $slot" >/dev/null 2>&1 + sleep 3 + fi + done + + # Continue to test for WAN connectivity while in this loop. If it comes back up, break out of the loop and reset VPN + if [ "$($timeoutcmd$timeoutsec nvram get wan0_state_t)" -ne 2 ] && [ "$($timeoutcmd$timeoutsec nvram get wan1_state_t)" -ne 2 ] + then + # Continue to loop and retest the WAN every 15 seconds + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: WAN DOWN" >> $logfile + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} Router is Currently Experiencing a WAN Down Situation ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Router is unable to provide a stable WAN connection. Please reboot your modem,${CClear}" + echo -e "${InvGreen} ${CClear} check with your ISP, or perform general internet connectivity troubleshooting${CClear}" + echo -e "${InvGreen} ${CClear} in order to re-establish a stable VPN connection.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} [Retrying to resume normal operations every 60 seconds]${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo "" + spinner 60 + wandownbreakertrip=1 + else + wandownbreakertrip=2 + break + fi + done + fi + + # If the WAN was down, and now it has just reset, then run a VPN Reset, and try to establish a new VPN connection + if [ "$wandownbreakertrip" = "2" ] + then + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WAN Link Detected -- Trying to reconnect/Reset VPN" >> $logfile + wandownbreakertrip=0 + clear + echo -e "${InvGreen} ${InvDkGray}${CWhite} VPNMON-R3 is currently recovering from a WAN Down Situation ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Router has detected a WAN Link/Modem and waiting 300 seconds for general network${CClear}" + echo -e "${InvGreen} ${CClear} connectivity to stabilize before re-establishing VPN/WG connectivity.${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} [Retrying to resume normal operations in 300 seconds...Please stand by!]${CClear}" + echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" + echo "" + spinner 300 + #sendmessage 1 "Recovering from WAN Down" - this doesn't work when the internet is down + exec sh /jffs/scripts/vpnmon-r3.sh -noswitch + fi + done +} + +# ------------------------------------------------------------------------------------------------------------------------- +##----------------------------------------## +## Modified by Martinski W. [2024-Oct-18] ## +##----------------------------------------## +# wancheck is a function that checks each wan connection to see if its active, and performs a ping and a city lookup... +wancheck() +{ + WANIF="$1" + WANIFNAME="$(get_wan_setting ifname)" + DUALWANMODE="$($timeoutcmd$timeoutsec nvram get wans_mode)" + + # If WAN 0 or 1 is connected, then proceed, else display that it's inactive + if [ "$WANIF" = "0" ] + then + if [ "$($timeoutcmd$timeoutsec nvram get wan0_state_t)" -eq 2 ] + then + # Call the get_wan_setting function courtesy of @dave14305 and using this interface name to ping and get a city name from + WAN0IFNAME="$(get_wan_setting0 ifname)" + + # Ping through the WAN interface # + if [ "$WANIFNAME" = "$WAN0IFNAME" ] || [ "$DUALWANMODE" = "lb" ] + then + WAN0PING="$(ping -I "$WAN0IFNAME" -c 1 "$PINGHOST" 2>/dev/null | awk -F'[/=]' 'END{print $5}')" + ## No need to do left-padding with zeros for alignment ## + [ -n "${WAN0PING:+xSETx}" ] && WAN0PING="$(printf "[%8.3f]" "$WAN0PING")" + else + WAN0PING="[FAILOVER]" + fi + + # On the rare occasion where it's unable to get the Ping time, show ERROR # + if [ -z "$WAN0PING" ] ; then WAN0PING="${CRed}[PING ERR]${CClear}" ; fi + + # Get the public IP of the WAN, determine the city from it, and display it on screen # + if [ -z "${WAN0IP:+xSETx}" ] + then + WAN0IP="$(curl --silent --retry 3 --retry-delay 2 --retry-all-errors --fail --interface "$WAN0IFNAME" --request GET --url https://ipv4.icanhazip.com)" + WAN0CITY="curl --silent --retry 3 --retry-delay 2 --retry-all-errors --request GET --url http://ip-api.com/json/$WAN0IP | jq --raw-output .city" + WAN0CITY="$(eval $WAN0CITY)" + if [ -z "$WAN0CITY" ] || echo "$WAN0CITY" | grep -qoE '\b(error.*:.*True.*|Undefined)\b' ; then WAN0CITY="$WAN0IP" ; fi + WAN0IP="$(printf '%15s' "$WAN0IP")" + fi + + # Insert bogus IP and City if screenshotmode is on + if [ "$screenshotmode" = "1" ] + then + WAN0CITY="Metropolis" + WAN0IP="$(printf '%15s' "11.22.33.44")" + fi + + if [ "$WAN0PING" = "[FAILOVER]" ] + then + echo -en "${InvGreen} ${InvDkGray}${CWhite} WAN0${CClear} | ${CGreen}[X]${CClear} | " + printf "%-6s" "$WAN0IFNAME" + echo -e " | ${CGreen}[ OK ]${CClear} | Failover | $WAN0IP | $WAN0PING | $WAN0CITY" + else + echo -en "${InvGreen} ${InvDkGray}${CWhite} WAN0${CClear} | ${CGreen}[X]${CClear} | " + printf "%-6s" "$WAN0IFNAME" + echo -e " | ${CGreen}[ OK ]${CClear} | Active | $WAN0IP | $WAN0PING | $WAN0CITY" + fi + else + echo -e "${InvDkGray}${CWhite} WAN0${CClear} | ${CGreen}[X]${CClear} | ${CDkGray}[n/a]${CClear} | ${CDkGray}[n/a ]${CClear} | Inactive | ${CDkGray}[n/a]${CClear} | ${CDkGray}[n/a]${CClear} | ${CDkGray}[n/a]${CClear}" + fi + fi + + if [ "$WANIF" = "1" ] + then + if [ "$($timeoutcmd$timeoutsec nvram get wan1_state_t)" -eq 2 ] + then + # Call the get_wan_setting function courtesy of @dave14305 and using this interface name to ping and get a city name from + WAN1IFNAME="$(get_wan_setting1 ifname)" + + # Ping through the WAN interface # + if [ "$WANIFNAME" = "$WAN1IFNAME" ] || [ "$DUALWANMODE" = "lb" ] + then + WAN1PING="$(ping -I "$WAN1IFNAME" -c 1 "$PINGHOST" 2>/dev/null | awk -F'[/=]' 'END{print $5}')" + ## No need to do left-padding with zeros for alignment ## + [ -n "${WAN1PING:+xSETx}" ] && WAN1PING="$(printf "[%8.3f]" "$WAN1PING")" + else + WAN1PING="[FAILOVER]" + fi + + # On the rare occasion where it's unable to get the Ping time, show ERROR # + if [ -z "$WAN1PING" ] ; then WAN1PING="${CRed}[PING ERR]${CClear}" ; fi + + # Get the public IP of the WAN, determine the city from it, and display it on screen # + if [ -z "${WAN1IP:+xSETx}" ] + then + WAN1IP="$(curl --silent --retry 3 --retry-delay 2 --retry-all-errors --fail --interface "$WAN1IFNAME" --request GET --url https://ipv4.icanhazip.com)" + WAN1CITY="curl --silent --retry 3 --retry-delay 2 --retry-all-errors --request GET --url http://ip-api.com/json/$WAN1IP | jq --raw-output .city" + WAN1CITY="$(eval $WAN1CITY)" + if [ -z "$WAN1CITY" ] || echo "$WAN1CITY" | grep -qoE '\b(error.*:.*True.*|Undefined)\b' ; then WAN1CITY="$WAN1IP" ; fi + WAN1IP="$(printf '%15s' "$WAN1IP")" + fi + + if [ "$WAN1PING" = "[FAILOVER]" ] + then + echo -en "${InvGreen} ${InvDkGray}${CWhite} WAN1${CClear} | ${CGreen}[X]${CClear} | " + printf "%-6s" "$WAN1IFNAME" + echo -e " | ${CGreen}[ OK ]${CClear} | Failover | $WAN1IP | $WAN1PING | $WAN1CITY" + else + echo -en "${InvGreen} ${InvDkGray}${CWhite} WAN1${CClear} | ${CGreen}[X]${CClear} | " + printf "%-6s" "$WAN1IFNAME" + echo -e " | ${CGreen}[ OK ]${CClear} | Active | $WAN1IP | $WAN1PING | $WAN1CITY" + fi + else + echo -e "${InvDkGray}${CWhite} WAN1${CClear} | ${CGreen}[X]${CClear} | ${CDkGray}[n/a]${CClear} | ${CDkGray}[n/a ]${CClear} | Inactive | ${CDkGray}[n/a]${CClear} | ${CDkGray}[n/a]${CClear} | ${CDkGray}[n/a]${CClear}" + fi + fi +} + +# ------------------------------------------------------------------------------------------------------------------------- + +# This function was "borrowed" graciously from @dave14305 from his FlexQoS script to determine the active WAN connection. +# Thanks much for your troubleshooting help as we tackled how to best derive the active WAN interface, Dave! + +get_wan_setting() +{ + local varname varval + varname="${1}" + prefixes="wan0_ wan1_" + + if [ "$($timeoutcmd$timeoutsec nvram get wans_mode)" = "lb" ] ; then + for prefix in $prefixes; do + state="$($timeoutcmd$timeoutsec nvram get "${prefix}"state_t)" + sbstate="$($timeoutcmd$timeoutsec nvram get "${prefix}"sbstate_t)" + auxstate="$($timeoutcmd$timeoutsec nvram get "${prefix}"auxstate_t)" + + # is_wan_connect() + [ "${state}" = "2" ] || continue + [ "${sbstate}" = "0" ] || continue + [ "${auxstate}" = "0" ] || [ "${auxstate}" = "2" ] || continue + + # get_wan_ifname() + proto="$($timeoutcmd$timeoutsec nvram get "${prefix}"proto)" + if [ "${proto}" = "pppoe" ] || [ "${proto}" = "pptp" ] || [ "${proto}" = "l2tp" ] ; then + varval="$($timeoutcmd$timeoutsec nvram get "${prefix}"pppoe_"${varname}")" + else + varval="$($timeoutcmd$timeoutsec nvram get "${prefix}""${varname}")" + fi + done + else + for prefix in $prefixes; do + primary="$($timeoutcmd$timeoutsec nvram get "${prefix}"primary)" + [ "${primary}" = "1" ] && break + done + + proto="$($timeoutcmd$timeoutsec nvram get "${prefix}"proto)" + if [ "${proto}" = "pppoe" ] || [ "${proto}" = "pptp" ] || [ "${proto}" = "l2tp" ] ; then + varval="$($timeoutcmd$timeoutsec nvram get "${prefix}"pppoe_"${varname}")" + else + varval="$($timeoutcmd$timeoutsec nvram get "${prefix}""${varname}")" + fi + fi + printf "%s" "${varval}" +} + +# Separated these out to get the ifname for a dual-wan/failover situation where both WAN connections are on +get_wan_setting0() +{ + local varname varval + varname="${1}" + prefixes="wan0_" + + if [ "$($timeoutcmd$timeoutsec nvram get wans_mode)" = "lb" ] ; then + for prefix in $prefixes; do + state="$($timeoutcmd$timeoutsec nvram get "${prefix}"state_t)" + sbstate="$($timeoutcmd$timeoutsec nvram get "${prefix}"sbstate_t)" + auxstate="$($timeoutcmd$timeoutsec nvram get "${prefix}"auxstate_t)" + + # is_wan_connect() + [ "${state}" = "2" ] || continue + [ "${sbstate}" = "0" ] || continue + [ "${auxstate}" = "0" ] || [ "${auxstate}" = "2" ] || continue + + # get_wan_ifname() + proto="$($timeoutcmd$timeoutsec nvram get "${prefix}"proto)" + if [ "${proto}" = "pppoe" ] || [ "${proto}" = "pptp" ] || [ "${proto}" = "l2tp" ] ; then + varval="$($timeoutcmd$timeoutsec nvram get "${prefix}"pppoe_"${varname}")" + else + varval="$($timeoutcmd$timeoutsec nvram get "${prefix}""${varname}")" + fi + done + else + for prefix in $prefixes; do + primary="$($timeoutcmd$timeoutsec nvram get "${prefix}"primary)" + [ "${primary}" = "1" ] && break + done + + proto="$($timeoutcmd$timeoutsec nvram get "${prefix}"proto)" + if [ "${proto}" = "pppoe" ] || [ "${proto}" = "pptp" ] || [ "${proto}" = "l2tp" ] ; then + varval="$($timeoutcmd$timeoutsec nvram get "${prefix}"pppoe_"${varname}")" + else + varval="$($timeoutcmd$timeoutsec nvram get "${prefix}""${varname}")" + fi + fi + printf "%s" "${varval}" +} + +# Separated these out to get the ifname for a dual-wan/failover situation where both WAN connections are on +get_wan_setting1() +{ + local varname varval + varname="${1}" + prefixes="wan1_" + + if [ "$($timeoutcmd$timeoutsec nvram get wans_mode)" = "lb" ] ; then + for prefix in $prefixes; do + state="$($timeoutcmd$timeoutsec nvram get "${prefix}"state_t)" + sbstate="$($timeoutcmd$timeoutsec nvram get "${prefix}"sbstate_t)" + auxstate="$($timeoutcmd$timeoutsec nvram get "${prefix}"auxstate_t)" + + # is_wan_connect() + [ "${state}" = "2" ] || continue + [ "${sbstate}" = "0" ] || continue + [ "${auxstate}" = "0" ] || [ "${auxstate}" = "2" ] || continue + + # get_wan_ifname() + proto="$($timeoutcmd$timeoutsec nvram get "${prefix}"proto)" + if [ "${proto}" = "pppoe" ] || [ "${proto}" = "pptp" ] || [ "${proto}" = "l2tp" ] ; then + varval="$($timeoutcmd$timeoutsec nvram get "${prefix}"pppoe_"${varname}")" + else + varval="$($timeoutcmd$timeoutsec nvram get "${prefix}""${varname}")" + fi + done + else + for prefix in $prefixes; do + primary="$($timeoutcmd$timeoutsec nvram get "${prefix}"primary)" + [ "${primary}" = "1" ] && break + done + + proto="$($timeoutcmd$timeoutsec nvram get "${prefix}"proto)" + if [ "${proto}" = "pppoe" ] || [ "${proto}" = "pptp" ] || [ "${proto}" = "l2tp" ] ; then + varval="$($timeoutcmd$timeoutsec nvram get "${prefix}"pppoe_"${varname}")" + else + varval="$($timeoutcmd$timeoutsec nvram get "${prefix}""${varname}")" + fi + fi + printf "%s" "${varval}" +} + +# ------------------------------------------------------------------------------------------------------------------------- +# This function displays the operations menu + +##----------------------------------------## +## Modified by Martinski W. [2024-Oct-05] ## +##----------------------------------------## +displayopsmenu() +{ + #scheduler colors and indicators + if [ "$schedule" = "0" ] + then + schedtime="${CDkGray}01:00${CClear}" + elif [ "$schedule" = "1" ] + then + schedhrs="$(printf "%02d" "$schedulehrs")" + schedmin="$(printf "%02d" "$schedulemin")" + schedtime="${CGreen}$schedhrs:$schedmin${CClear}" + fi + + #autostart colors and indicators + if [ "$autostart" = "0" ]; then + rebootprot="${CDkGray}Disabled${CClear}" + elif [ "$autostart" = "1" ]; then + rebootprot="${CGreen}Enabled${CClear}" + fi + + if [ "$logsize" -eq 0 ]; then + logSizeStr="${CDkGray}n/a${CClear}" + else + logSizeStr="${CGreen}${logsize}${CClear}" + fi + + if [ -z "$timerloop" ]; then + timerLoopStr="${CDkGray}n/a${CClear}" + else + timerLoopStr="${CGreen}$timerloop sec${CClear}" + fi + + if [ "$pingreset" -eq 0 ]; then + pingResetStr="${CDkGray}Disabled${CClear}" + else + pingResetStr="${CGreen}$pingreset ms${CClear}" + fi + + if [ "$amtmemailsuccess" = "0" ] && [ "$amtmemailfailure" = "0" ]; then + amtmdisp="${CDkGray}Disabled " + elif [ "$amtmemailsuccess" = "1" ] && [ "$amtmemailfailure" = "0" ]; then + amtmdisp="${CGreen}Success " + elif [ "$amtmemailsuccess" = "0" ] && [ "$amtmemailfailure" = "1" ]; then + amtmdisp="${CGreen}Failure " + elif [ "$amtmemailsuccess" = "1" ] && [ "$amtmemailfailure" = "1" ]; then + amtmdisp="${CGreen}Success, Failure" + else + amtmdisp="${CDkGray}Disabled " + fi + + if [ "$ratelimit" = "0" ]; then + rldisp="${CRed}RL" + else + rldisp="${CGreen}RL:$ratelimit/h" + fi + + #display operations menu + if [ "$availableslots" = "1 2" ] + then + echo -e "${InvGreen} ${InvDkGray}${CWhite} Operations Menu ${CClear}" + echo -e "${InvGreen} ${CClear} Reset/Reconnect VPN 1:${CGreen}(1)${CClear} 2:${CGreen}(2)${CClear} ${InvGreen} ${CClear} ${CGreen}(C)${CClear}onfiguration Menu / Main Setup Menu | $rldisp${CClear}" + echo -e "${InvGreen} ${CClear} Stop/Unmonitor VPN 1:${CGreen}(!)${CClear} 2:${CGreen}(@)${CClear} ${InvGreen} ${CClear} ${CGreen}(R)${CClear}eset VPN/WG CRON Time Scheduler: $schedtime" + echo -e "${InvGreen} ${CClear} Enable/Disable ${CGreen}(M)${CClear}onitored VPN Slots ${InvGreen} ${CClear} ${CGreen}(L)${CClear}og Viewer / Trim Log Size (rows): $logSizeStr" + echo -e "${InvGreen} ${CClear} Update/Maintain ${CGreen}(V)${CClear}PN Server Lists ${InvGreen} ${CClear} ${CGreen}(A)${CClear}utostart VPNMON-R3 on Reboot: $rebootprot" + echo -e "${InvGreen} ${CClear} Edit/R${CGreen}(U)${CClear}n Server List Automation ${InvGreen} ${CClear} ${CGreen}(T)${CClear}imer VPN Check Loop Interval: $timerLoopStr" + echo -e "${InvGreen} ${CClear} AMTM Email Not${CGreen}(I)${CClear}fications: $amtmdisp ${InvGreen} ${CClear} ${CGreen}(P)${CClear}ing Maximum Before Reset in ms: $pingResetStr" + echo -e "${InvGreen} ${CClear}${CDkGray}--------------------------------------------------------------------------------------------------------------${CClear}" + echo "" + elif [ "$availableslots" = "1 2 3 4 5" ] + then + echo -e "${InvGreen} ${InvDkGray}${CWhite} Operations Menu ${CClear}" + echo -e "${InvGreen} ${CClear} Reset/Reconnect VPN 1:${CGreen}(1)${CClear} 2:${CGreen}(2)${CClear} 3:${CGreen}(3)${CClear} 4:${CGreen}(4)${CClear} 5:${CGreen}(5)${CClear} ${InvGreen} ${CClear} ${CGreen}(C)${CClear}onfiguration Menu / Main Setup Menu | $rldisp${CClear}" + echo -e "${InvGreen} ${CClear} Stop/Unmonitor VPN 1:${CGreen}(!)${CClear} 2:${CGreen}(@)${CClear} 3:${CGreen}(#)${CClear} 4:${CGreen}($)${CClear} 5:${CGreen}(%)${CClear} ${InvGreen} ${CClear} ${CGreen}(R)${CClear}eset VPN/WG CRON Time Scheduler: $schedtime" + echo -e "${InvGreen} ${CClear} Reset/Reconnect WG 1:${CGreen}(6)${CClear} 2:${CGreen}(7)${CClear} 3:${CGreen}(8)${CClear} 4:${CGreen}(9)${CClear} 5:${CGreen}(0)${CClear} ${InvGreen} ${CClear} ${CGreen}(L)${CClear}og Viewer / Trim Log Size (rows): $logSizeStr" + echo -e "${InvGreen} ${CClear} Stop/Unmonitor WG 1:${CGreen}(^)${CClear} 2:${CGreen}(&)${CClear} 3:${CGreen}(-)${CClear} 4:${CGreen}(+)${CClear} 5:${CGreen}(=)${CClear} ${InvGreen} ${CClear} ${CGreen}(A)${CClear}utostart VPNMON-R3 on Reboot: $rebootprot" + echo -e "${InvGreen} ${CClear} Enable/Disable ${CGreen}(M)${CClear}onitored VPN/WG Slots ${InvGreen} ${CClear} ${CGreen}(T)${CClear}imer VPN Check Loop Interval: $timerLoopStr" + echo -e "${InvGreen} ${CClear} Update/Maintain ${CGreen}(V)${CClear}PN/${CGreen}(W)${CClear}G Server Lists ${InvGreen} ${CClear} ${CGreen}(P)${CClear}ing Maximum Before Reset in ms: $pingResetStr" + echo -e "${InvGreen} ${CClear} Edit/R${CGreen}(U)${CClear}n Server List Automation ${InvGreen} ${CClear} AMTM Email Not${CGreen}(I)${CClear}fications: $amtmdisp" + echo -e "${InvGreen} ${CClear}${CDkGray}--------------------------------------------------------------------------------------------------------------${CClear}" + echo "" + fi +} + +# ------------------------------------------------------------------------------------------------------------------------- +# Begin VPNMON-R2 Main Loop +# ------------------------------------------------------------------------------------------------------------------------- + +#DEBUG=; set -x # uncomment/comment to enable/disable debug mode +#{ # uncomment/comment to enable/disable debug mode + +# Create the necessary folder/file structure for VPNMON-R3 under /jffs/addons +if [ ! -d "/jffs/addons/vpnmon-r3.d" ]; then + mkdir -p "/jffs/addons/vpnmon-r3.d" +fi + +# Check for and add an alias for VPNMON-R3 +if ! grep -F "sh /jffs/scripts/vpnmon-r3.sh" /jffs/configs/profile.add >/dev/null 2>/dev/null; then + echo "alias vpnmon-r3=\"sh /jffs/scripts/vpnmon-r3.sh\" # added by vpnmon-r3" >> /jffs/configs/profile.add +fi + +##-------------------------------------## +## Added by Martinski W. [2024-Oct-05] ## +##-------------------------------------## +_SetUpTimeoutCmdVars_ +_SetLAN_HostName_ + +##-------------------------------------## +## Added by Martinski W. [2024-Nov-04] ## +##-------------------------------------## +ROUTERMODEL="$($timeoutcmd$timeoutsec nvram get odmpid)" +[ -z "$ROUTERMODEL" ] && ROUTERMODEL="$($timeoutcmd$timeoutsec nvram get productid)" +FWVER="$($timeoutcmd$timeoutsec nvram get firmver | tr -d '.')" +BUILDNO="$($timeoutcmd$timeoutsec nvram get buildno)" +EXTENDNO="$($timeoutcmd$timeoutsec nvram get extendno)" +if [ -z "$EXTENDNO" ]; then EXTENDNO=0; fi +FWBUILD="${FWVER}.${BUILDNO}_${EXTENDNO}" + +# Check for updates +updatecheck + +# Check and see if any commandline option is being used +if [ $# -eq 0 ] || [ -z "$1" ] +then + clear + exec sh /jffs/scripts/vpnmon-r3.sh -noswitch + exit 0 +fi + +# Check and see if an invalid commandline option is being used +if [ "$1" = "-h" ] || [ "$1" = "-help" ] || [ "$1" = "-setup" ] || [ "$1" = "-reset" ] || [ "$1" = "-bw" ] || [ "$1" = "-noswitch" ] || [ "$1" = "-screen" ] || [ "$1" = "-now" ] +then + clear +else + clear + echo "" + echo "VPNMON-R3 v$version" + echo "" + echo "Exiting due to invalid commandline options!" + echo "(run 'vpnmon-r3 -h' for help)" + echo "" + echo -e "${CClear}" + exit 0 +fi + +# Check to see if the help option is being called +if [ "$1" = "-h" ] || [ "$1" = "-help" ] +then + clear + echo "" + echo "VPNMON-R3 v$version Commandline Option Usage:" + echo "" + echo "vpnmon-r3 -h | -help" + echo "vpnmon-r3 -setup" + echo "vpnmon-r3 -reset" + echo "vpnmon-r3 -bw" + echo "vpnmon-r3 -screen" + echo "vpnmon-r3 -screen -now" + echo "" + echo " -h | -help (this output)" + echo " -setup (displays the setup menu)" + echo " -reset (resets vpn connections and exits)" + echo " -bw (runs vpnmon-r3 in monochrome mode)" + echo " -screen (runs vpnmon-r3 in screen background)" + echo " -screen -now (runs vpnmon-r3 in screen background immediately)" + echo "" + echo -e "${CClear}" + exit 0 +fi + +# Check to see if a second command is being passed to remove color +if [ "$1" = "-bw" ] || { [ $# -gt 1 ] && [ "$2" = "-bw" ] ; } +then + blackwhite +fi + +# Check to see if the -now parameter is being called to bypass the screen timer +if [ $# -gt 1 ] && [ "$2" = "-now" ] +then + bypassscreentimer=1 +fi + +# Check to see if the setup option is being called +if [ "$1" = "-setup" ] +then + logoNM + vsetup + exit 0 +fi + +# Check to see if the reset option is being called +if [ "$1" = "-reset" ] +then + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: VPN Reset initiated through -RESET switch" >> $logfile + vreset +fi + +# Check to see if the screen option is being called and run operations normally using the screen utility +if [ "$1" = "-screen" ] +then + screen -wipe >/dev/null 2>&1 # Kill any dead screen sessions + sleep 1 + ScreenSess=$(screen -ls | grep "vpnmon-r3" | awk '{print $1}' | cut -d . -f 1) + if [ -z $ScreenSess ]; then + if [ "$bypassscreentimer" = "1" ]; then + screen -dmS "vpnmon-r3" $apppath -noswitch + sleep 1 + screen -r vpnmon-r3 + else + clear + echo -e "${CClear}Executing ${CGreen}VPNMON-R3 v$version${CClear} using the SCREEN utility..." + echo "" + echo -e "${CClear}IMPORTANT:" + echo -e "${CClear}In order to keep VPNMON-R3 running in the background," + echo -e "${CClear}properly exit the SCREEN session by using: ${CGreen}CTRL-A + D${CClear}" + echo "" + screen -dmS "vpnmon-r3" $apppath -noswitch + sleep 5 + screen -r vpnmon-r3 + exit 0 + fi + else + if [ "$bypassscreentimer" = "1" ]; then + sleep 1 + else + clear + echo -e "${CClear}Connecting to existing ${CGreen}VPNMON-R3 v$version${CClear} SCREEN session...${CClear}" + echo "" + echo -e "${CClear}IMPORTANT:${CClear}" + echo -e "${CClear}In order to keep VPNMON-R3 running in the background,${CClear}" + echo -e "${CClear}properly exit the SCREEN session by using: ${CGreen}CTRL-A + D${CClear}" + echo "" + echo -e "${CClear}Switching to the SCREEN session in T-5 sec...${CClear}" + echo -e "${CClear}" + spinner 5 + fi + fi + screen -dr $ScreenSess + exit 0 +fi + +# Check to see if the noswitch option is being called +if [ "$1" = "-noswitch" ] +then + clear #last switch before the main program starts + firstrun=1 + + # Clean up lockfile + rm -f $lockfile >/dev/null 2>&1 + + if [ ! -f "$config" ] && [ ! -f "/opt/bin/timeout" ] && [ ! -f "/opt/sbin/screen" ] && [ ! -f "/opt/bin/jq" ] + then + echo -e "${CRed}ERROR: VPNMON-R3 is not configured. Please run 'vpnmon-r3 -setup' first.${CClear}" + echo "" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: VPNMON-R3 is not configured. Please run the setup/configuration utility" >> $logfile + exit 0 + fi +fi + +ubsync="" +firstDataCollection=true + +##----------------------------------------## +## Modified by Martinski W. [2024-Nov-02] ## +##----------------------------------------## +while true +do + _SetUpTimeoutCmdVars_ + _SetLAN_HostName_ + + # Grab the VPNMON-R3 config file and read it in + if [ -f "$config" ] + then + source "$config" + else + clear + echo -e "${CRed}ERROR: VPNMON-R3 is not configured. Please run 'vpnmon-r3.sh -setup' first." + echo "" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: VPNMON-R3 config file not found. Please run the setup/configuration utility" >> $logfile + echo -e "${CClear}" + exit 1 + fi + + # Grab the monitored slots file and read it in + if [ -f /jffs/addons/vpnmon-r3.d/vr3clients.txt ] + then + source /jffs/addons/vpnmon-r3.d/vr3clients.txt + else + clear + echo -e "${CRed}ERROR: VPNMON-R3 is not configured. Please run 'vpnmon-r3.sh -setup' first." + echo "" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: VPNMON-R3 VPN Client Monitoring file not found. Please run the setup/configuration utility" >> $logfile + echo -e "${CClear}" + exit 1 + fi + + createconfigs + + # Grab the monitored slots file and read it in + if [ -f /jffs/addons/vpnmon-r3.d/vr3timers.txt ] + then + source /jffs/addons/vpnmon-r3.d/vr3timers.txt + else + clear + echo -e "${CRed}ERROR: VPNMON-R3 is not configured. Please run 'vpnmon-r3.sh -setup' first." + echo "" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: VPNMON-R3 VPN Timer Monitoring file not found. Please run the setup/configuration utility" >> $logfile + echo -e "${CClear}" + exit 1 + fi + + #Set variables + resetvpn=0 + resetwg=0 + + #Check to see if a reset is currently underway + lockcheck + + clear #display the header + + if [ "$hideoptions" = "0" ] && [ "$hideoptions" != "$prevHideOpts" ] + then + timerreset=0 + displayopsmenu + else + timerreset=0 + fi + prevHideOpts="$hideoptions" + + tzone="$(date +%Z)" + tzonechars="${#tzone}" + + if [ "$tzonechars" = "1" ]; then tzspaces=" "; + elif [ "$tzonechars" = "2" ]; then tzspaces=" "; + elif [ "$tzonechars" = "3" ]; then tzspaces=" "; + elif [ "$tzonechars" = "4" ]; then tzspaces=" "; + elif [ "$tzonechars" = "5" ]; then tzspaces=" "; fi + + #Display VPNMON-R3 client header + echo -en "${InvGreen} ${InvDkGray}${CWhite} VPNMON-R3 - v" + printf "%-8s" $version + echo -e " ${CGreen}(S)${CWhite}how/${CGreen}(H)${CWhite}ide Operations Menu ${InvDkGray} $tzspaces$(date) ${CClear}" + + #Display VPNMON-R2 Found Warning + if [ -f /jffs/scripts/vpnmon-r2.sh ]; then + echo -e "${InvYellow} ${InvRed}${CWhite} VPNMON-R2 Install Detected. Support and availability for R2 is being sunset. Press (X) for Uninstall Menu. ${CClear}" + fi + + #Display VPNMON-R3 Update Notifications + if [ "$UpdateNotify" != "0" ]; then echo -e "$UpdateNotify\n"; else echo -e "${CClear}"; fi + + #If WAN Monitoring is enabled, test WAN connection and show the following grid + if [ "$monitorwan" = "1" ] && [ "$firstrun" = "1" ] + then + #Check to see if the WAN is up + checkwan + firstrun=0 + fi + + if [ "$monitorwan" = "1" ] && [ "$firstrun" = "0" ] + then + #Display WAN ports grid + echo -e "${CClear} Port | Mon | IFace | Health | WAN State | Public WAN IP | Ping-->WAN | City Exit" + echo -e "-------|-----|--------|--------|--------------|-----------------|------------|---------------------------------" + + #Cycle through the WANCheck connection function to display ping/city info + wans=0 + for wans in 0 1 + do + wancheck "$wans" + done + echo -e "-------|-----|--------|--------|--------------|-----------------|------------|---------------------------------" + echo "" + fi + + if [ "$useovpn" = "1" ] + then + echo -e "${InvDkGray} OpenVPN ${CClear}" + echo "" + #Display VPN client slot grid + if [ "$unboundclient" != "0" ]; then + echo -e " Slot | Mon | Svrs | Health | VPN State | Public VPN IP | Ping-->VPN | City Exit / Time / UB" + else + echo -e " Slot | Mon | Svrs | Health | VPN State | Public VPN IP | Ping-->VPN | City Exit / Time" + fi + echo -e "-------|-----|--------|--------|--------------|-----------------|------------|---------------------------------" + + if "$firstDataCollection" ; then printf "\r\033[0K${InvYellow} ${CClear} Please wait..." ; sleep 1 ; fi + + i=0 + for i in $availableslots #loop through the VPN slots + do + #Set variables + citychange="" + + #determine if the slot is monitored# + if [ "$((VPN$i))" = "1" ]; then + monitored="${CGreen}[X]${CClear}" + else + monitored="[ ]" + fi + + #determine the vpn state, and if connected, get vpn IP and city + vpnstate="$(_VPN_GetClientState_ "$i")" + + if [ "$vpnstate" = "0" ] + then + vpnstate="Disconnected" + vpnhealth="${CDkGray}[n/a ]${CClear}" + vpnindicator="${InvDkGray} ${CClear}" + vpnip=" ${CDkGray}[n/a]${CClear}" + vpncity="${CDkGray}[n/a]${CClear}" + svrping=" ${CDkGray}[n/a]${CClear}" + elif [ "$vpnstate" = "-1" ] + then + vpnstate="Error State " + vpnhealth="${CDkGray}[n/a ]${CClear}" + vpnindicator="${InvDkGray} ${CClear}" + vpnip="${CDkGray} [n/a]${CClear}" + vpncity="${CDkGray}[n/a]${CClear}" + svrping=" ${CDkGray}[n/a]${CClear}" + elif [ "$vpnstate" = "1" ] + then + vpnstate="Connecting " + vpnhealth="${CDkGray}[n/a ]${CClear}" + vpnindicator="${InvYellow} ${CClear}" + vpnip=" ${CDkGray}[n/a]${CClear}" + vpncity="${CDkGray}[n/a]${CClear}" + svrping=" ${CDkGray}[n/a]${CClear}" + elif [ "$vpnstate" = "2" ] + then + vpnstate="Connected " + checkvpn "$i" + getvpnip "$i" + getvpncity "$i" + if [ -z "$vpnping" ] + then + svrping="${CRed}[PING ERR]${CClear}" + vpnhealth="${CYellow}[UNKN]${CClear}" + vpnindicator="${InvYellow} ${CClear}" + else + ## No need to do left-padding with zeros for alignment ## + svrping="$(printf "[%8.3f]" "$vpnping")" + fi + else + vpnstate="Unknown " + vpnhealth="${CDkGray}[n/a ]${CClear}" + vpnindicator="${InvDkGray} ${CClear}" + vpnip=" ${CDkGray}[n/a]${CClear}" + vpncity="${CDkGray}[n/a]${CClear}" + svrping=" ${CDkGray}[n/a]${CClear}" + fi + + #Determine how many server entries are in each of the vpn slot alternate server files# + if [ -s "/jffs/addons/vpnmon-r3.d/vr3svr$i.txt" ] + then + servercnt="$(cat "/jffs/addons/vpnmon-r3.d/vr3svr$i.txt" | wc -l)" + if [ -z "$servercnt" ] || [ "$servercnt" -lt 1 ] + then + servercnt="${CRed}[0000]${CClear}" + else + ## No need to do left-padding with zeros for alignment ## + servercnt="$(printf "[%4d]" "$servercnt")" + fi + else + servercnt="${CRed}[0000]${CClear}" + fi + + #Calculate connected time for current VPN slot + if [ $((VPNTIMER$i)) = "0" ] || [ "$((VPN$i))" = "0" ] + then + sincelastreset="" + else + currtime=$(date +%s) + timediff=$((currtime-VPNTIMER$i)) + sincelastreset=$(printf ': %dd %02dh:%02dm\n' $(($timediff/86400)) $(($timediff%86400/3600)) $(($timediff%3600/60))) + fi + + if "$firstDataCollection" ; then printf "\r\033[0K" ; firstDataCollection=false ; fi + + # Print the results of all data gathered sofar # + echo -e "$vpnindicator${InvDkGray}${CWhite} VPN$i${CClear} | $monitored | $servercnt | $vpnhealth | $vpnstate | $vpnip | $svrping | $vpncity$sincelastreset $citychange$ubsync" + + #if a vpn is monitored and disconnected, try to restart it + if [ "$((VPN$i))" = "1" ] && [ "$vpnstate" = "Disconnected" ] + then #reconnect + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: VPN$i has disconnected" >> $logfile + echo "" + printf "\33[2K\r" + + #Display a standard timer# + timer=0 + while [ $timer -ne 5 ] + do + timer="$((timer+1))" + preparebar 46 "|" + progressbarpause $timer 5 "" "s" "Standard" + done + printf "\33[2K\r" + + restartvpn $i + sendmessage 1 "VPN Tunnel Disconnected" $i + restartrouting + resetspdmerlin + exec sh /jffs/scripts/vpnmon-r3.sh -noswitch + fi + + #if a vpn is monitored and in error state, try to restart it + if [ "$((VPN$i))" = "1" ] && [ "$vpnstate" = "Error State " ] + then #reconnect + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: VPN$i is in an error state and being reconnected" >> $logfile + echo "" + printf "\33[2K\r" + + #display a standard timer# + timer=0 + while [ $timer -ne 5 ] + do + timer="$((timer+1))" + preparebar 46 "|" + progressbarpause $timer 5 "" "s" "Standard" + #sleep 1 + done + printf "\33[2K\r" + + restartvpn $i + sendmessage 1 "VPN Slot In Error State" $i + restartrouting + resetspdmerlin + exec sh /jffs/scripts/vpnmon-r3.sh -noswitch + fi + + #if a vpn is monitored and not responsive, try to restart it + if [ "$((VPN$i))" = "1" ] && [ "$resetvpn" != "0" ] + then #reconnect + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: VPN$i is non-responsive and being reconnected" >> $logfile + echo "" + printf "\33[2K\r" + + #display a standard timer# + timer=0 + while [ $timer -ne 5 ] + do + timer="$((timer+1))" + preparebar 46 "|" + progressbarpause $timer 5 "" "s" "Standard" + #sleep 1 + done + printf "\33[2K\r" + + restartvpn $resetvpn + sendmessage 1 "VPN Slot Is Non-Responsive" $resetvpn + restartrouting + resetspdmerlin + exec sh /jffs/scripts/vpnmon-r3.sh -noswitch + fi + + # if a vpn connection ping is greater than a certain amount, restart it + maxsvrping=$(awk "BEGIN {printf \"%3.0f\", ${vpnping}}") >/dev/null 2>&1 + MP=$? + if [ $MP -ne 0 ]; then + maxsvrping=0 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: Invalid VPN PING information received." >> $logfile + fi + + if [ "$pingreset" -gt 0 ] + then + if [ "$maxsvrping" -ge "$pingreset" ] + then + echo "" + printf "\33[2K\r" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: VPN$i PING exceeds max allowed ($pingreset ms)" >> $logfile + printf "${CGreen}\r[Maximum PING Exceeded]" + sleep 3 + printf "\33[2K\r" + + #display a standard timer# + timer=0 + while [ $timer -ne 5 ] + do + timer="$((timer+1))" + preparebar 46 "|" + progressbarpause $timer 5 "" "s" "Standard" + #sleep 1 + done + printf "\33[2K\r" + + restartvpn $i + sendmessage 1 "VPN Slot Exceeded Max Ping" $i + restartrouting + resetspdmerlin + exec sh /jffs/scripts/vpnmon-r3.sh -noswitch + fi + fi + + #Reset variables + ubsync="" + sincelastreset="" + + done + + echo -e "-------|-----|--------|--------|--------------|-----------------|------------|---------------------------------" + echo "" + fi + +#-----------------Wireguard + + if [ "$availableslots" = "1 2 3 4 5" ] && [ "$usewg" = "1" ]; then + + echo -e "${InvDkGray} Wireguard ${CClear}" + echo "" + + #Display WG client slot grid + echo -e " Slot | Mon | Svrs | Health | WG State | Public WG IP | Ping--->WG | City Exit / Time" + echo -e "-------|-----|--------|--------|--------------|-----------------|------------|---------------------------------" + + i=0 + for i in $availableslots #loop through the VPN slots + do + #Set variables + wgcitychange="" + + #determine if the slot is monitored# + if [ "$((WG$i))" = "1" ]; then + wgmonitored="${CGreen}[X]${CClear}" + else + wgmonitored="[ ]" + fi + + #determine the vpn state, and if connected, get vpn IP and city + wgstate="$(_WG_GetClientState_ "$i")" + + if [ "$wgstate" = "0" ] + then + wgstate="Disconnected" + wghealth="${CDkGray}[n/a ]${CClear}" + wgindicator="${InvDkGray} ${CClear}" + wgip=" ${CDkGray}[n/a]${CClear}" + wgcity="${CDkGray}[n/a]${CClear}" + wgsvrping=" ${CDkGray}[n/a]${CClear}" + elif [ "$wgstate" = "2" ] + then + wgstate="Connected " + checkwg "$i" + getwgip "$i" + getwgcity "$i" + if [ -z "$wgping" ] + then + wgsvrping="${CRed}[PING ERR]${CClear}" + wghealth="${CYellow}[UNKN]${CClear}" + wgindicator="${InvYellow} ${CClear}" + else + ## No need to do left-padding with zeros for alignment ## + wgsvrping="$(printf "[%8.3f]" "$wgping")" + fi + else + wgstate="Unknown " + wghealth="${CDkGray}[n/a ]${CClear}" + wgindicator="${InvDkGray} ${CClear}" + wgip=" ${CDkGray}[n/a]${CClear}" + wgcity="${CDkGray}[n/a]${CClear}" + wgsvrping=" ${CDkGray}[n/a]${CClear}" + fi + + #Determine how many server entries are in each of the vpn slot alternate server files# + if [ -s "/jffs/addons/vpnmon-r3.d/vr3wgsvr$i.txt" ] + then + wgservercnt="$(cat "/jffs/addons/vpnmon-r3.d/vr3wgsvr$i.txt" | wc -l)" + if [ -z "$wgservercnt" ] || [ "$wgservercnt" -lt 1 ] + then + wgservercnt="${CRed}[0000]${CClear}" + else + ## No need to do left-padding with zeros for alignment ## + wgservercnt="$(printf "[%4d]" "$wgservercnt")" + fi + else + wgservercnt="${CRed}[0000]${CClear}" + fi + + #Calculate connected time for current VPN slot + if [ $((WGTIMER$i)) = "0" ] || [ "$((WG$i))" = "0" ] + then + wgsincelastreset="" + else + wgcurrtime=$(date +%s) + wgtimediff=$((wgcurrtime-WGTIMER$i)) + wgsincelastreset=$(printf ': %dd %02dh:%02dm\n' $(($wgtimediff/86400)) $(($wgtimediff%86400/3600)) $(($wgtimediff%3600/60))) + fi + + #if "$firstDataCollection" ; then printf "\r\033[0K" ; firstDataCollection=false ; fi + + # Print the results of all data gathered sofar # + echo -e "$wgindicator${InvDkGray}${CWhite} WG$i${CClear} | $wgmonitored | $wgservercnt | $wghealth | $wgstate | $wgip | $wgsvrping | $wgcity$wgsincelastreset $wgcitychange" + + #if a wg connection is monitored and disconnected, try to restart it + if [ "$((WG$i))" = "1" ] && [ "$wgstate" = "Disconnected" ] + then #reconnect + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WG$i has disconnected" >> $logfile + echo "" + printf "\33[2K\r" + + #Display a standard timer# + timer=0 + while [ $timer -ne 5 ] + do + timer="$((timer+1))" + preparebar 46 "|" + progressbarpause $timer 5 "" "s" "Standard" + done + printf "\33[2K\r" + + restartwg $i + sendmessage 1 "WG Tunnel Disconnected" $i + restartrouting + resetspdmerlin + exec sh /jffs/scripts/vpnmon-r3.sh -noswitch + fi + + #if the wg handshake exceeds 200s, try to restart it + # Inspiration from ZebMcKayHan's WGC Watchdog Script + if [ "$((WG$i))" = "1" ] && [ "$wgstate" = "Connected" ] + then + last_handshake=$(wg show wgc$i latest-handshakes | awk '{print $2}') >/dev/null 2>&1 + if [ ! -z $last_handshake ] + then + idle_seconds=$((`date +%s`-${last_handshake})) + if [ "$idle_seconds" -gt "200" ] + then #reconnect + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WG$i handshake exceeded 200s" >> $logfile + echo "" + printf "\33[2K\r" + + #Display a standard timer# + timer=0 + while [ $timer -ne 5 ] + do + timer="$((timer+1))" + preparebar 46 "|" + progressbarpause $timer 5 "" "s" "Standard" + done + printf "\33[2K\r" + + restartwg $i + sendmessage 1 "WG Handshake Exceeded" $i + restartrouting + resetspdmerlin + exec sh /jffs/scripts/vpnmon-r3.sh -noswitch + fi + fi + fi + + # if a wg connection ping is greater than a certain amount, restart it + maxsvrping=$(awk "BEGIN {printf \"%3.0f\", ${wgping}}") >/dev/null 2>&1 + MP=$? + if [ $MP -ne 0 ]; then + maxsvrping=0 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: Invalid WG PING information received." >> $logfile + fi + + if [ "$pingreset" -gt 0 ] + then + if [ "$maxsvrping" -ge "$pingreset" ] + then + echo "" + printf "\33[2K\r" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WG$i PING exceeds max allowed ($pingreset ms)" >> $logfile + printf "${CGreen}\r[Maximum PING Exceeded]" + sleep 3 + printf "\33[2K\r" + + #display a standard timer# + timer=0 + while [ $timer -ne 5 ] + do + timer="$((timer+1))" + preparebar 46 "|" + progressbarpause $timer 5 "" "s" "Standard" + #sleep 1 + done + printf "\33[2K\r" + + restartwg $i + sendmessage 1 "WG Slot Exceeded Max Ping" $i + restartrouting + resetspdmerlin + exec sh /jffs/scripts/vpnmon-r3.sh -noswitch + fi + fi + + #if a wg is monitored and not responsive, try to restart it + if [ "$((WG$i))" = "1" ] && [ "$resetwg" != "0" ] + then #reconnect + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WG$i is non-responsive and being reconnected" >> $logfile + echo "" + printf "\33[2K\r" + + #display a standard timer# + timer=0 + while [ $timer -ne 5 ] + do + timer="$((timer+1))" + preparebar 46 "|" + progressbarpause $timer 5 "" "s" "Standard" + #sleep 1 + done + printf "\33[2K\r" + + restartwg $resetwg + sendmessage 1 "WG Slot Is Non-Responsive" $resetwg + restartrouting + resetspdmerlin + exec sh /jffs/scripts/vpnmon-r3.sh -noswitch + fi + + #Reset variables + wgsincelastreset="" + + done + + echo -e "-------|-----|--------|--------|--------------|-----------------|------------|---------------------------------" + echo "" + + #----------------- + fi + + #display a standard timer# + timer=0 + lastTimerSec=0 + updateTimer=true + + while [ "$timer" -lt "$timerloop" ] + do + if "$updateTimer" + then + updateTimer=false + timer="$((timer+1))" + lastTimerSec="$(date +%s)" + fi + preparebar 46 "|" + progressbaroverride "$timer" "$timerloop" "" "s" "Standard" + lockcheck #Check to see if a reset is currently underway + if [ "$timerreset" = "1" ]; then timer="$timerloop" ; fi + + ## Prevent repeatedly fast key presses from updating the timer ## + [ "$(date +%s)" -gt "$lastTimerSec" ] && updateTimer=true + done + + #Check to see if a reset is currently underway + lockcheck + prevHideOpts=X + + #if Unbound is active and out of sync, try to restart it + if [ "$unboundclient" != "0" ] && [ "$ResolverTimer" = "1" ] && [ "$useovpn" = "1" ] + then + printf "\33[2K\r" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: VPN$unboundreset is out of sync with Unbound DNS Resolver" >> $logfile + printf "${CGreen}\r[Unbound is out of sync]" + sleep 3 + printf "\33[2K\r" + + #display a standard timer + timer=0 + while [ $timer -ne 5 ] + do + timer="$((timer+1))" + preparebar 46 "|" + progressbarpause $timer 5 "" "s" "Standard" + done + printf "\33[2K\r" + + restartvpn $unboundreset + sendmessage 1 "VPN Slot Not Synced With Unbound" $unboundreset + restartrouting + resetspdmerlin + exec sh /jffs/scripts/vpnmon-r3.sh -noswitch + fi + + #Check to see if the WAN is up + if [ "$monitorwan" = "1" ] && [ "$bypasswancheck" = "0" ]; then + checkwan + fi + + firstrun=0 + +done +echo -e "${CClear}" +exit 0 + +#} #2>&1 | tee $LOG | logger -t $(basename $0)[$$] # uncomment/comment to enable/disable debug mode From e33c979cd27be32ac2eee4d99ee4de73305dae7a Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:07:49 +0000 Subject: [PATCH 10/48] Update version.txt --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index bc80560..b13a760 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.5.0 +1.6.0b1 From bf74a3e13b95af54fbd2d9d65d97eaad78a8a902 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:08:12 +0000 Subject: [PATCH 11/48] Update enhancements.txt --- enhancements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/enhancements.txt b/enhancements.txt index 9637621..8ba1562 100644 --- a/enhancements.txt +++ b/enhancements.txt @@ -1,10 +1,11 @@ To-Do: - If the WAN doesn't respond after X minutes, issue a router reboot? - Specify how many times you want cycle to go before a reset occurs (right now it's every cycle, but could make it 2 or 3?) -- Get unbound over WG working + Done: - Remove RL tag if amtm email functionality is turned off completely, and show some info in the config menu on the AMTM email item. +- Get unbound over WG working - Option to show/hide OVPN and WG sections. @Stephen Harrington - Wireguard compatibility From f5312cb07ca873451a6fa8f569c4ec9218b9f426 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:15:59 +0000 Subject: [PATCH 12/48] Update changelog.txt --- changelog.txt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index b8def4b..349d9e1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,10 @@ -v1.5.1 - (TBA) +v1.6.0b1 - (TBA) + - MINOR: Very happy to announce that "Unbound-over-Wireguard" has actually become a reality! With huge + help and guidance from @ZebMcKayhan, a methodology was developed that is easier, simpler and much more + elegant than it's Unbound-over-VPN twin. In essence, VPNMON-R3 creates a new virtual br0 interface, + ties that to the WG client of your choice, and assigns that virtual br0 IP as the designated outgoing + interface. Should the Wireguard client happen to go down or stop, Unbound will continue to resolve DNS + directly over the WAN. - PATCH: Based on a recommendation from @JGrana, the RL (Email Rate Limiting) indicators will no longer show up on the main UI (or in the config display) when AMTM email functionality is turned off. From c541ece9b1f0366a55c6903f736602e88f23eb27 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:16:42 +0000 Subject: [PATCH 13/48] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e78659e..d43dd1b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# VPNMON-R3 v1.5.0 +# VPNMON-R3 v1.6.0 Beta 1 Asus-Merlin OpenVPN/Wireguard Monitor R3 -Updated on 2025-Aug-03 +Updated on 2025-Aug-11 --- From 3e160cb4a03c5e941efc3b8c6804f57ab99f20b3 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:17:26 +0000 Subject: [PATCH 14/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index 8be6903..4cd2161 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -7,7 +7,7 @@ # are connected, and sends a ping to a host of your choice through each active connection. If it finds that a connection # has been lost, it will execute a series of commands that will kill that single VPN client, and randomly picks one of # your specified servers to reconnect to for each VPN client. -# Last Modified: 2025-Aug-3 +# Last Modified: 2025-Aug-11 ########################################################################################## #Preferred standard router binaries path From 6927f914d9d86f3ca33d407fb00c735783100d49 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Mon, 11 Aug 2025 15:38:32 +0000 Subject: [PATCH 15/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index 4cd2161..5d3a399 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -1090,6 +1090,17 @@ do ;; 4) + + if [ "$unboundwgclient" -ge 1 ]; then + echo "" + echo -e "${CRed}Unbound-over-WG is currently enabled. If you want to enable Unbound-over-VPN, please proceed to 'Disable'" + echo -e "the Unbound-over-WG option first, then choose to enable Unbound-over-VPN.${CClear}" + echo "" + sleep 3 + read -rsp $'Press any key to continue...\n' -n1 key + continue + fi + while true; do clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Unbound DNS Lookups over VPN Integration ${CClear}" @@ -1146,7 +1157,7 @@ do unboundclient=0 saveconfig sleep 3 - break + continue fi if [ "$unboundovervpn" = "0" ] || [ "$unboundovervpn" = "1" ] || [ "$unboundovervpn" = "2" ] || [ "$unboundovervpn" = "3" ] || [ "$unboundovervpn" = "4" ] || [ "$unboundovervpn" = "5" ] || [ "$unboundovervpn" = "e" ]; then @@ -1183,7 +1194,7 @@ do unboundclient=0 saveconfig sleep 3 - break + continue elif [ "$unboundovervpn" = "1" ] || [ "$unboundovervpn" = "2" ] || [ "$unboundovervpn" = "3" ] || [ "$unboundovervpn" = "4" ] || [ "$unboundovervpn" = "5" ]; then @@ -1195,7 +1206,7 @@ do echo "" echo -e "${CRed}When changing a VPN Client Slot (from Slot #$unboundclient to Slot #$unboundovervpn), please proceed to 'Disable'" echo -e "first (option 0), then choose a new VPN Slot." - sleep 5; break + sleep 5; continue fi # Modify or create post-mount @@ -1267,7 +1278,7 @@ do saveconfig echo "Please reboot your router now if this is your first time or re-enabled Unbound over VPN" read -rsp $'Press any key to continue...\n' -n1 key - break + continue elif [ "$unboundovervpn" = "e" ]; then echo -e "${CClear}\n[Exiting]"; sleep 2; break @@ -1280,6 +1291,17 @@ do ;; 5) + + if [ "$unboundclient" -ge 1 ]; then + echo "" + echo -e "${CRed}Unbound-over-VPN is currently enabled. If you want to enable Unbound-over-WG, please proceed to 'Disable'" + echo -e "the Unbound-over-VPN option first, then choose to enable Unbound-over-WG.${CClear}" + echo "" + sleep 3 + read -rsp $'Press any key to continue...\n' -n1 key + continue + fi + while true; do clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Unbound DNS Lookups over WG Integration ${CClear}" @@ -1335,7 +1357,7 @@ do unboundwgclient=0 saveconfig sleep 3 - break + continue fi if [ "$unboundoverwg" = "0" ] || [ "$unboundoverwg" = "1" ] || [ "$unboundoverwg" = "2" ] || [ "$unboundoverwg" = "3" ] || [ "$unboundoverwg" = "4" ] || [ "$unboundoverwg" = "5" ] || [ "$unboundoverwg" = "e" ]; then @@ -1361,7 +1383,7 @@ do echo -e "${CRed}Error: Unbound Configuration file not found or not readable at ${UNBOUND_CONF}${CClear}" echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Unbound Configuration file not found or not readable at ${UNBOUND_CONF}" >> $logfile sleep 3 - break + continue fi # Create a temp file to move unbound.conf contents into @@ -1379,7 +1401,7 @@ do echo -e "${CRed}No changes have been made to ${UNBOUND_CONF}.${CClear}" echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Failed to modify configuration. The temporary file is empty or was not created." >> $logfile sleep 3 - break + continue fi # Move the temp file into the unbound.conf file @@ -1427,7 +1449,7 @@ do unboundwgclient=0 saveconfig sleep 3 - break + continue elif [ "$unboundoverwg" = "1" ] || [ "$unboundoverwg" = "2" ] || [ "$unboundoverwg" = "3" ] || [ "$unboundoverwg" = "4" ] || [ "$unboundoverwg" = "5" ]; then @@ -1442,7 +1464,7 @@ do echo "" sleep 3 read -rsp $'Press any key to continue...\n' -n1 key - break + continue fi # =============================================== @@ -1477,7 +1499,7 @@ do echo -e "${CRed}Error: Unbound Configuration file not found or not readable at ${UNBOUND_CONF}${CClear}" echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Unbound Configuration file not found or not readable at ${UNBOUND_CONF}" >> $logfile sleep 3 - break + continue fi # Create a temp file to move unbound.conf contents into @@ -1493,7 +1515,7 @@ do echo -e "${CRed}No changes have been made to ${UNBOUND_CONF}.${CClear}" echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Failed to modify configuration. The temporary file is empty or was not created." >> $logfile sleep 3 - break + continue fi # Move the temp file into the unbound.conf file @@ -1534,7 +1556,7 @@ do echo "" sleep 3 read -rsp $'Press any key to continue...\n' -n1 key - break + continue elif [ "$unboundoverwg" = "e" ]; then echo -e "${CClear}\n[Exiting]"; sleep 2; break From 45d5ab2a872b1ab8d444610c26fff1491a49fa26 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Wed, 13 Aug 2025 00:09:23 +0000 Subject: [PATCH 16/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 118 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 114 insertions(+), 4 deletions(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index 5d3a399..6476baa 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -1,20 +1,20 @@ #!/bin/sh -# VPNMON-R3 v1.6.0b1 (VPNMON-R3.SH) is an all-in-one script that is optimized to maintain multiple VPN connections and is +# VPNMON-R3 v1.6.0b2 (VPNMON-R3.SH) is an all-in-one script that is optimized to maintain multiple VPN connections and is # able to provide for the capabilities to randomly reconnect using a specified server list containing the servers of your # choice. Special care has been taken to ensure that only the VPN connections you want to have monitored are tended to. # This script will check the health of up to 5 VPN connections on a regular interval to see if monitored VPN conenctions # are connected, and sends a ping to a host of your choice through each active connection. If it finds that a connection # has been lost, it will execute a series of commands that will kill that single VPN client, and randomly picks one of # your specified servers to reconnect to for each VPN client. -# Last Modified: 2025-Aug-11 +# Last Modified: 2025-Aug-12 ########################################################################################## #Preferred standard router binaries path export PATH="/sbin:/bin:/usr/sbin:/usr/bin:$PATH" #Static Variables - please do not change -version="1.6.0b1" # Version tracker +version="1.6.0b2" # Version tracker beta=1 # Beta switch screenshotmode=0 # Switch to present bogus info for screenshots apppath="/jffs/scripts/vpnmon-r3.sh" # Static path to the app @@ -1445,10 +1445,31 @@ do echo -e "${CGreen}[Removed virtual br0 interface at 10.99.88.77]...${CClear}" sleep 1 + # Remove Unbound failsafe in init-start + if [ -f /jffs/scripts/init-start ]; then + sed -i -e '/vpnmon-r3/d' /jffs/scripts/init-start + fi + echo "" + echo -e "${CGreen}[Unbound failsafe removed from init-start file]...${CClear}" + sleep 1 + + # Remove Unbound failsafe in nat-start + if [ -f /jffs/scripts/nat-start ]; then + sed -i -e '/vpnmon-r3/d' /jffs/scripts/nat-start + fi + echo "" + echo -e "${CGreen}[Unbound failsafe removed from nat-start file]...${CClear}" + sleep 1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Unbound-over-WG was removed from VPNMON-R3" >> $logfile unboundwgclient=0 saveconfig + + echo "" + echo "Please consider rebooting your router now after major Unbound and system file changes." + echo "" sleep 3 + read -rsp $'Press any key to continue...\n' -n1 key continue elif [ "$unboundoverwg" = "1" ] || [ "$unboundoverwg" = "2" ] || [ "$unboundoverwg" = "3" ] || [ "$unboundoverwg" = "4" ] || [ "$unboundoverwg" = "5" ]; then @@ -1547,10 +1568,47 @@ do sleep 3 fi + echo "" + echo -e "${CGreen}[Adding Unbound Failsafe... Modifying init-start file]...${CClear}" + sleep 1 + + # Modify or create init-start to ensure survivability after a router reboot + if [ -f /jffs/scripts/init-start ]; then + + if ! grep -q -F "sh /jffs/scripts/vpnmon-r3.sh -uowginitstart" /jffs/scripts/init-start; then + echo "sh /jffs/scripts/vpnmon-r3.sh -uowginitstart # Added by vpnmon-r3" >> /jffs/scripts/init-start + fi + + else + echo "#!/bin/sh" > /jffs/scripts/init-start + echo "" >> /jffs/scripts/init-start + echo "sh /jffs/scripts/vpnmon-r3.sh -uowginitstart # Added by vpnmon-r3" >> /jffs/scripts/init-start + chmod 755 /jffs/scripts/init-start + fi + + echo "" + echo -e "${CGreen}[Adding Unbound Failsafe... Modifying nat-start file]...${CClear}" + sleep 1 + + # Modify or create nat-start to ensure survivability after a router reboot + if [ -f /jffs/scripts/nat-start ]; then + + if ! grep -q -F "sh /jffs/scripts/vpnmon-r3.sh -uowgnatstart" /jffs/scripts/nat-start; then + echo "sh /jffs/scripts/vpnmon-r3.sh -uowgnatstart # Added by vpnmon-r3" >> /jffs/scripts/nat-start + fi + + else + echo "#!/bin/sh" > /jffs/scripts/nat-start + echo "" >> /jffs/scripts/nat-start + echo "sh /jffs/scripts/vpnmon-r3.sh -uowgnatstart # Added by vpnmon-r3" >> /jffs/scripts/nat-start + chmod 755 /jffs/scripts/nat-start + fi + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Unbound-over-WG was enabled for VPNMON-R3" >> $logfile echo -e "${CClear}" unboundwgclient=$unboundoverwg saveconfig + echo "" echo "Please consider rebooting your router now if this is your first time or have re-enabled Unbound-over-WG" echo "" @@ -1829,6 +1887,44 @@ do done } +# ------------------------------------------------------------------------------------------------------------------------- +# uowginitstart is a function that initializes the virtual br0 interface when called from init-start + +uowginitstart() +{ + + if [ -f "$config" ] + then + source "$config" + + # Remove added virtual br0 interface if it already exists + ifconfig br0:UnboundWG down >/dev/null 2>&1 + + # Addvirtual br0 interface + ifconfig br0:UnboundWG 10.99.88.77 netmask 255.255.255.255 up >/dev/null 2>&1 + fi + +} + +# ------------------------------------------------------------------------------------------------------------------------- +# uowgnatstart is a function that ties the virtual br0 interface to the assigned wgc slot + +uowgnatstart() +{ + + if [ -f "$config" ] + then + source "$config" + + # Remove ip rule if it already exists + ip rule del prio 11 >/dev/null 2>&1 + + # Add ip rule to tie br0 interface with assigned wgc slot + ip rule add from 10.99.88.77 lookup wgc$unboundwgclient prio 11 >/dev/null 2>&1 + fi + +} + # ------------------------------------------------------------------------------------------------------------------------- # vupdate is a function that provides a UI to check for script updates and allows you to install the latest version... @@ -5848,7 +5944,7 @@ then fi # Check and see if an invalid commandline option is being used -if [ "$1" = "-h" ] || [ "$1" = "-help" ] || [ "$1" = "-setup" ] || [ "$1" = "-reset" ] || [ "$1" = "-bw" ] || [ "$1" = "-noswitch" ] || [ "$1" = "-screen" ] || [ "$1" = "-now" ] +if [ "$1" = "-h" ] || [ "$1" = "-help" ] || [ "$1" = "-setup" ] || [ "$1" = "-reset" ] || [ "$1" = "-bw" ] || [ "$1" = "-noswitch" ] || [ "$1" = "-screen" ] || [ "$1" = "-now" ] || [ "$1" = "-uowginitstart" ] || [ "$1" = "-uowgnatstart" ] then clear else @@ -5888,6 +5984,20 @@ then exit 0 fi +# Check to see if the Unbound-over-WG init-start action is being called +if [ "$1" = "-uowginitstart" ] +then + uowginitstart + exit 0 +fi + +# Check to see if the Unbound-over-WG nat-start action is being called +if [ "$1" = "-uowgnatstart" ] +then + uowgnatstart + exit 0 +fi + # Check to see if a second command is being passed to remove color if [ "$1" = "-bw" ] || { [ $# -gt 1 ] && [ "$2" = "-bw" ] ; } then From e7a2649375a194491e6bfebbab2dc7ad62e13fd3 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Wed, 13 Aug 2025 00:09:45 +0000 Subject: [PATCH 17/48] Update vpnmon-r3.sh From d2500d2c7015e5c8ded5637a0c3b820bd8d8ae1c Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Wed, 13 Aug 2025 00:09:59 +0000 Subject: [PATCH 18/48] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d43dd1b..1279ad3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# VPNMON-R3 v1.6.0 Beta 1 +# VPNMON-R3 v1.6.0 Beta 2 Asus-Merlin OpenVPN/Wireguard Monitor R3 -Updated on 2025-Aug-11 +Updated on 2025-Aug-12 --- From ee864f84634916bc6d5cf37e6335c1ea5a79eb7f Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Wed, 13 Aug 2025 00:12:49 +0000 Subject: [PATCH 19/48] Update changelog.txt --- changelog.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 349d9e1..6ed5d4e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,4 @@ -v1.6.0b1 - (TBA) +v1.6.0b2 - (TBA) - MINOR: Very happy to announce that "Unbound-over-Wireguard" has actually become a reality! With huge help and guidance from @ZebMcKayhan, a methodology was developed that is easier, simpler and much more elegant than it's Unbound-over-VPN twin. In essence, VPNMON-R3 creates a new virtual br0 interface, @@ -7,6 +7,10 @@ v1.6.0b1 - (TBA) directly over the WAN. - PATCH: Based on a recommendation from @JGrana, the RL (Email Rate Limiting) indicators will no longer show up on the main UI (or in the config display) when AMTM email functionality is turned off. + - PATCH: Thanks to the keen eye of @ZebMcKayhan, important failsafe's for Unbound were not yet being + addressed. This version adds those failsafes. If you're coming from Beta 1, please make sure you + (1) update your script to Beta 2, (2) Disable Unbound over WG, (3) Re-enable Unbound over WG. This + process will then write the appropriate commands to your init-start and nat-start files. v1.5.0 - (August 3, 2025) - MINOR: Well, actually kinda major. Wireguard is now formally being supported! VPNMON-R3 will show all From bf3b5fdd53a577cdd0351b607126be8cdffc9506 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Thu, 14 Aug 2025 01:07:54 +0000 Subject: [PATCH 20/48] Update changelog.txt --- changelog.txt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 6ed5d4e..f7abdb1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,4 @@ -v1.6.0b2 - (TBA) +v1.6.0b3 - (TBA) - MINOR: Very happy to announce that "Unbound-over-Wireguard" has actually become a reality! With huge help and guidance from @ZebMcKayhan, a methodology was developed that is easier, simpler and much more elegant than it's Unbound-over-VPN twin. In essence, VPNMON-R3 creates a new virtual br0 interface, @@ -11,6 +11,14 @@ v1.6.0b2 - (TBA) addressed. This version adds those failsafes. If you're coming from Beta 1, please make sure you (1) update your script to Beta 2, (2) Disable Unbound over WG, (3) Re-enable Unbound over WG. This process will then write the appropriate commands to your init-start and nat-start files. + - PATCH: Implemented Recovery Timeout Opportunities, they're kind of like second chances to allow your + connection to recover before issuing a restart. By default, connections will restart after your timer + loop finishes (default: 60 sec). The Recovery Timeout would extend the number of loops before it + would issue a reset, and hopefully within that time, would recover the connection on its own. For + example, your timer loop = 60 seconds. Your Recovery Timeout is set to 3x. This would mean if your + connection doesn't recover in 3 minutes, then it will issue a reset. This option might help bring + down the amount of times your connection resets throughout the day, but then there's also a greater + chance your connection might become unusable for a slighly longer amount of time. v1.5.0 - (August 3, 2025) - MINOR: Well, actually kinda major. Wireguard is now formally being supported! VPNMON-R3 will show all From 289223d9ef621fb3568fdc28afd6ebb33b49cdb6 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Thu, 14 Aug 2025 01:08:44 +0000 Subject: [PATCH 21/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 372 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 219 insertions(+), 153 deletions(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index 6476baa..b0658cc 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -1,20 +1,20 @@ #!/bin/sh -# VPNMON-R3 v1.6.0b2 (VPNMON-R3.SH) is an all-in-one script that is optimized to maintain multiple VPN connections and is +# VPNMON-R3 v1.6.0b3 (VPNMON-R3.SH) is an all-in-one script that is optimized to maintain multiple VPN connections and is # able to provide for the capabilities to randomly reconnect using a specified server list containing the servers of your # choice. Special care has been taken to ensure that only the VPN connections you want to have monitored are tended to. # This script will check the health of up to 5 VPN connections on a regular interval to see if monitored VPN conenctions # are connected, and sends a ping to a host of your choice through each active connection. If it finds that a connection # has been lost, it will execute a series of commands that will kill that single VPN client, and randomly picks one of # your specified servers to reconnect to for each VPN client. -# Last Modified: 2025-Aug-12 +# Last Modified: 2025-Aug-13 ########################################################################################## #Preferred standard router binaries path export PATH="/sbin:/bin:/usr/sbin:/usr/bin:$PATH" #Static Variables - please do not change -version="1.6.0b2" # Version tracker +version="1.6.0b3" # Version tracker beta=1 # Beta switch screenshotmode=0 # Switch to present bogus info for screenshots apppath="/jffs/scripts/vpnmon-r3.sh" # Static path to the app @@ -26,6 +26,7 @@ vr3emails="/jffs/addons/vpnmon-r3.d/vr3emails.txt" # Static path to availableslots="1 2 3 4 5" # Available slots tracker logsize=2000 # Log file size in rows timerloop=60 # Timer loop in sec +recover=3 # Number of loops to allow for connection recovery schedule=0 # Scheduler enable y/n schedulehrs=1 # Scheduler hours schedulemin=0 # Scheduler mins @@ -51,6 +52,9 @@ timeoutsec="" # For "timeout" timeoutlng="" # For "timeout" cmd for "nvram" calls hideoptions=1 # Hide/Show menu options flag ratelimit=0 # Rate limiting number of emails/houre +vrcnt=0 # Counter for VPN connection recovery +wrcnt=0 # Counter for WG connection recovery + ##-------------------------------------## ## Added by Martinski W. [2024-Oct-05] ## @@ -873,12 +877,12 @@ do else unboundwgclientexp="Enabled, WGC$unboundwgclient" fi - + if [ "$unboundclient" -eq 0 ] && [ "$unboundwgclient" -eq 0 ]; then - unboundshowip=0 - saveconfig + unboundshowip=0 + saveconfig fi - + if [ "$unboundshowip" -eq 0 ]; then unboundshowipdisp="Disabled" else @@ -1090,7 +1094,7 @@ do ;; 4) - + if [ "$unboundwgclient" -ge 1 ]; then echo "" echo -e "${CRed}Unbound-over-WG is currently enabled. If you want to enable Unbound-over-VPN, please proceed to 'Disable'" @@ -1100,7 +1104,7 @@ do read -rsp $'Press any key to continue...\n' -n1 key continue fi - + while true; do clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Unbound DNS Lookups over VPN Integration ${CClear}" @@ -1291,7 +1295,7 @@ do ;; 5) - + if [ "$unboundclient" -ge 1 ]; then echo "" echo -e "${CRed}Unbound-over-VPN is currently enabled. If you want to enable Unbound-over-WG, please proceed to 'Disable'" @@ -1301,7 +1305,7 @@ do read -rsp $'Press any key to continue...\n' -n1 key continue fi - + while true; do clear echo -e "${InvGreen} ${InvDkGray}${CWhite} Unbound DNS Lookups over WG Integration ${CClear}" @@ -1368,78 +1372,78 @@ do echo -e "${CGreen}[Disabling Unbound over WG]...${CClear}" sleep 2 - # =============================================== - # === Disable Unbound-over-WG Integration === - # === Methodology engineered by @ZebMcKayhan === - # =============================================== + # =============================================== + # === Disable Unbound-over-WG Integration === + # === Methodology engineered by @ZebMcKayhan === + # =============================================== # Modify unbound.conf file - set major variables UNBOUND_CONF="/opt/var/lib/unbound/unbound.conf" DISABLED_LINE="#outgoing-interface: 10.0.20.1 # v1.08 Martineau Use VPN tunnel to hide Root server queries from ISP (or force WAN ONLY)" - # Check if the unbound.conf file exists and is readable. - if [ ! -r "${UNBOUND_CONF}" ]; then - echo "" - echo -e "${CRed}Error: Unbound Configuration file not found or not readable at ${UNBOUND_CONF}${CClear}" + # Check if the unbound.conf file exists and is readable. + if [ ! -r "${UNBOUND_CONF}" ]; then + echo "" + echo -e "${CRed}Error: Unbound Configuration file not found or not readable at ${UNBOUND_CONF}${CClear}" echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Unbound Configuration file not found or not readable at ${UNBOUND_CONF}" >> $logfile - sleep 3 - continue - fi - + sleep 3 + continue + fi + # Create a temp file to move unbound.conf contents into TMP_FILE="${UNBOUND_CONF}.$$" - + # Remove outgoing-interface contents and replace with a dummy line sed "s/^[[:space:]]*#*[[:space:]]*outgoing-interface:.*/${DISABLED_LINE}/g" "${UNBOUND_CONF}" > "${TMP_FILE}" echo "" echo -e "${CGreen}[Disabled 'outgoing-interface' and set back to default]...${CClear}" sleep 1 - # Ensure the temporary file was created and is not empty before overwriting the original - if [ ! -s "${TMP_FILE}" ]; then - echo -e "${CRed}Error: Failed to modify configuration. The temporary file is empty or was not created.${CClear}" - echo -e "${CRed}No changes have been made to ${UNBOUND_CONF}.${CClear}" + # Ensure the temporary file was created and is not empty before overwriting the original + if [ ! -s "${TMP_FILE}" ]; then + echo -e "${CRed}Error: Failed to modify configuration. The temporary file is empty or was not created.${CClear}" + echo -e "${CRed}No changes have been made to ${UNBOUND_CONF}.${CClear}" echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Failed to modify configuration. The temporary file is empty or was not created." >> $logfile - sleep 3 - continue - fi + sleep 3 + continue + fi - # Move the temp file into the unbound.conf file - mv "${TMP_FILE}" "${UNBOUND_CONF}" >/dev/null 2>&1 + # Move the temp file into the unbound.conf file + mv "${TMP_FILE}" "${UNBOUND_CONF}" >/dev/null 2>&1 echo "" echo -e "${CGreen}[Modified unbound.conf file back to defaults]...${CClear}" sleep 1 - # Restart unbound service - if [ -f "/jffs/addons/unbound/unbound_manager.sh" ]; then - unbound_manager restart >/dev/null 2>&1 - RESTART_STATUS=$? - if [ ${RESTART_STATUS} -eq 0 ]; then - echo "" - echo -e "${CGreen}[Unbound service restarted successfully]...${CClear}" - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Unbound service restarted successfully" >> $logfile - sleep 1 - else - echo "" - echo -e "${CRed}[Warning: 'unbound_manager restart' command failed with status ${RESTART_STATUS}]...${CClear}" - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: 'unbound_manager restart' command failed with status ${RESTART_STATUS}" >> $logfile - sleep 3 - fi - else - echo "" - echo -e "${CRed}[Warning: 'unbound_manager.sh' script not found. Could not restart service]...${CClear}" - echo -e "${CRed}[Please restart the unbound service manually]...${CClear}" - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: 'unbound_manager.sh' script not found. Could not restart service" >> $logfile - sleep 3 - fi - - # Remove the IP rule from VPN Director - ip rule del prio 11 >/dev/null 2>&1 - echo "" + # Restart unbound service + if [ -f "/jffs/addons/unbound/unbound_manager.sh" ]; then + unbound_manager restart >/dev/null 2>&1 + RESTART_STATUS=$? + if [ ${RESTART_STATUS} -eq 0 ]; then + echo "" + echo -e "${CGreen}[Unbound service restarted successfully]...${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Unbound service restarted successfully" >> $logfile + sleep 1 + else + echo "" + echo -e "${CRed}[Warning: 'unbound_manager restart' command failed with status ${RESTART_STATUS}]...${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: 'unbound_manager restart' command failed with status ${RESTART_STATUS}" >> $logfile + sleep 3 + fi + else + echo "" + echo -e "${CRed}[Warning: 'unbound_manager.sh' script not found. Could not restart service]...${CClear}" + echo -e "${CRed}[Please restart the unbound service manually]...${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: 'unbound_manager.sh' script not found. Could not restart service" >> $logfile + sleep 3 + fi + + # Remove the IP rule from VPN Director + ip rule del prio 11 >/dev/null 2>&1 + echo "" echo -e "${CGreen}[Removed IP rule for virtual br0 interface at 10.99.88.77]...${CClear}" sleep 1 - - # Remove added virtual br0 interface + + # Remove added virtual br0 interface ifconfig br0:UnboundWG down >/dev/null 2>&1 echo "" echo -e "${CGreen}[Removed virtual br0 interface at 10.99.88.77]...${CClear}" @@ -1452,7 +1456,7 @@ do echo "" echo -e "${CGreen}[Unbound failsafe removed from init-start file]...${CClear}" sleep 1 - + # Remove Unbound failsafe in nat-start if [ -f /jffs/scripts/nat-start ]; then sed -i -e '/vpnmon-r3/d' /jffs/scripts/nat-start @@ -1464,7 +1468,7 @@ do echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Unbound-over-WG was removed from VPNMON-R3" >> $logfile unboundwgclient=0 saveconfig - + echo "" echo "Please consider rebooting your router now after major Unbound and system file changes." echo "" @@ -1488,15 +1492,15 @@ do continue fi - # =============================================== - # === Create Unbound-over-WG Integration === - # === Methodology engineered by @ZebMcKayhan === - # =============================================== + # =============================================== + # === Create Unbound-over-WG Integration === + # === Methodology engineered by @ZebMcKayhan === + # =============================================== echo "" echo -e "${CGreen}[Enabling Unbound over WG]...${CClear}" sleep 2 - + # Create new virtual br0 interface ifconfig br0:UnboundWG 10.99.88.77 netmask 255.255.255.255 up >/dev/null 2>&1 echo "" @@ -1513,60 +1517,60 @@ do UNBOUND_CONF="/opt/var/lib/unbound/unbound.conf" ENABLED_IP="10.99.88.77" COMMENT="# v1.08 Martineau Use VPN tunnel to hide Root server queries from ISP (or force WAN ONLY)" - - # Check if the unbound.conf file exists and is readable. - if [ ! -r "${UNBOUND_CONF}" ]; then - echo "" - echo -e "${CRed}Error: Unbound Configuration file not found or not readable at ${UNBOUND_CONF}${CClear}" + + # Check if the unbound.conf file exists and is readable. + if [ ! -r "${UNBOUND_CONF}" ]; then + echo "" + echo -e "${CRed}Error: Unbound Configuration file not found or not readable at ${UNBOUND_CONF}${CClear}" echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Unbound Configuration file not found or not readable at ${UNBOUND_CONF}" >> $logfile - sleep 3 - continue - fi - + sleep 3 + continue + fi + # Create a temp file to move unbound.conf contents into TMP_FILE="${UNBOUND_CONF}.$$" - + # Create new outgoing-interface contents ENABLED_LINE="outgoing-interface: ${ENABLED_IP} ${COMMENT}" sed "s/^[[:space:]]*#*[[:space:]]*outgoing-interface:.*/${ENABLED_LINE}/g" "${UNBOUND_CONF}" > "${TMP_FILE}" - # Ensure the temporary file was created and is not empty before overwriting the original - if [ ! -s "${TMP_FILE}" ]; then - echo -e "${CRed}Error: Failed to modify configuration. The temporary file is empty or was not created.${CClear}" - echo -e "${CRed}No changes have been made to ${UNBOUND_CONF}.${CClear}" + # Ensure the temporary file was created and is not empty before overwriting the original + if [ ! -s "${TMP_FILE}" ]; then + echo -e "${CRed}Error: Failed to modify configuration. The temporary file is empty or was not created.${CClear}" + echo -e "${CRed}No changes have been made to ${UNBOUND_CONF}.${CClear}" echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - ERROR: Failed to modify configuration. The temporary file is empty or was not created." >> $logfile - sleep 3 - continue - fi - - # Move the temp file into the unbound.conf file - mv "${TMP_FILE}" "${UNBOUND_CONF}" >/dev/null 2>&1 + sleep 3 + continue + fi + + # Move the temp file into the unbound.conf file + mv "${TMP_FILE}" "${UNBOUND_CONF}" >/dev/null 2>&1 echo "" echo -e "${CGreen}[Modified unbound.conf file 'outgoing-interface: 10.99.88.77']...${CClear}" sleep 1 - # Restart unbound service - if [ -f "/jffs/addons/unbound/unbound_manager.sh" ]; then - unbound_manager restart >/dev/null 2>&1 - RESTART_STATUS=$? - if [ ${RESTART_STATUS} -eq 0 ]; then - echo "" - echo -e "${CGreen}[Unbound service restarted successfully]...${CClear}" - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Unbound service restarted successfully" >> $logfile - sleep 1 - else - echo "" - echo -e "${CRed}[Warning: 'unbound_manager restart' command failed with status ${RESTART_STATUS}]...${CClear}" - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: 'unbound_manager restart' command failed with status ${RESTART_STATUS}" >> $logfile - sleep 3 - fi - else - echo "" - echo -e "${CRed}[Warning: 'unbound_manager.sh' script not found. Could not restart service]...${CClear}" - echo -e "${CRed}[Please restart the unbound service manually]...${CClear}" - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: 'unbound_manager.sh' script not found. Could not restart service" >> $logfile - sleep 3 - fi + # Restart unbound service + if [ -f "/jffs/addons/unbound/unbound_manager.sh" ]; then + unbound_manager restart >/dev/null 2>&1 + RESTART_STATUS=$? + if [ ${RESTART_STATUS} -eq 0 ]; then + echo "" + echo -e "${CGreen}[Unbound service restarted successfully]...${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Unbound service restarted successfully" >> $logfile + sleep 1 + else + echo "" + echo -e "${CRed}[Warning: 'unbound_manager restart' command failed with status ${RESTART_STATUS}]...${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: 'unbound_manager restart' command failed with status ${RESTART_STATUS}" >> $logfile + sleep 3 + fi + else + echo "" + echo -e "${CRed}[Warning: 'unbound_manager.sh' script not found. Could not restart service]...${CClear}" + echo -e "${CRed}[Please restart the unbound service manually]...${CClear}" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: 'unbound_manager.sh' script not found. Could not restart service" >> $logfile + sleep 3 + fi echo "" echo -e "${CGreen}[Adding Unbound Failsafe... Modifying init-start file]...${CClear}" @@ -1603,12 +1607,12 @@ do echo "sh /jffs/scripts/vpnmon-r3.sh -uowgnatstart # Added by vpnmon-r3" >> /jffs/scripts/nat-start chmod 755 /jffs/scripts/nat-start fi - + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Unbound-over-WG was enabled for VPNMON-R3" >> $logfile echo -e "${CClear}" unboundwgclient=$unboundoverwg saveconfig - + echo "" echo "Please consider rebooting your router now if this is your first time or have re-enabled Unbound-over-WG" echo "" @@ -1896,12 +1900,12 @@ uowginitstart() if [ -f "$config" ] then source "$config" - - # Remove added virtual br0 interface if it already exists - ifconfig br0:UnboundWG down >/dev/null 2>&1 - - # Addvirtual br0 interface - ifconfig br0:UnboundWG 10.99.88.77 netmask 255.255.255.255 up >/dev/null 2>&1 + + # Remove added virtual br0 interface if it already exists + ifconfig br0:UnboundWG down >/dev/null 2>&1 + + # Addvirtual br0 interface + ifconfig br0:UnboundWG 10.99.88.77 netmask 255.255.255.255 up >/dev/null 2>&1 fi } @@ -1915,12 +1919,12 @@ uowgnatstart() if [ -f "$config" ] then source "$config" - - # Remove ip rule if it already exists - ip rule del prio 11 >/dev/null 2>&1 - - # Add ip rule to tie br0 interface with assigned wgc slot - ip rule add from 10.99.88.77 lookup wgc$unboundwgclient prio 11 >/dev/null 2>&1 + + # Remove ip rule if it already exists + ip rule del prio 11 >/dev/null 2>&1 + + # Add ip rule to tie br0 interface with assigned wgc slot + ip rule add from 10.99.88.77 lookup wgc$unboundwgclient prio 11 >/dev/null 2>&1 fi } @@ -3570,15 +3574,26 @@ timerloopconfig() while true do clear - echo -e "${InvGreen} ${InvDkGray}${CWhite} Timer Loop Configuration ${CClear}" + echo -e "${InvGreen} ${InvDkGray}${CWhite} Timer Loop Configuration / Recovery Timeout Opportunities ${CClear}" + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Please indicate how long the timer cycle should take between Connectivity checks,${CClear}" + echo -e "${InvGreen} ${CClear} as well as the number of Recovery Timeout Opportunities the loop should complete${CClear}" + echo -e "${InvGreen} ${CClear} before issuing a connection reset.${CClear}" echo -e "${InvGreen} ${CClear}" - echo -e "${InvGreen} ${CClear} Please indicate how long the timer cycle should take between VPN Connection checks.${CClear}" - echo -e "${InvGreen} ${CClear} (Default = 60 seconds)${CClear}" + echo -e "${InvGreen} ${CClear} Recovery Timeout Opportunities are simply the number of chances the script will${CClear}" + echo -e "${InvGreen} ${CClear} give the connection to recover on its own before intervening. With this value${CClear}" + echo -e "${InvGreen} ${CClear} defaulting to 1, if connectivity issues occur within the default 60 second Timer${CClear}" + echo -e "${InvGreen} ${CClear} Loop, the connectivity will be reset after those 60 seconds." + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} Example: 60 Second Timer Loops with 3 Recovery Timeouts would reset the connection${CClear}" + echo -e "${InvGreen} ${CClear} after 3 minutes." + echo -e "${InvGreen} ${CClear}" + echo -e "${InvGreen} ${CClear} (Defaults: Timer Loop = 60 seconds | Recovery Timeouts = 1)${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" - echo -e "${InvGreen} ${CClear} Current: ${CGreen}$timerloop sec${CClear}" + echo -e "${InvGreen} ${CClear} Current: Timer Loop = ${CGreen}$timerloop sec ${CClear}| Recovery Timeouts: ${CGreen}$recover${CClear}" echo - read -p "Please enter value in seconds [5-999] (e=Exit): " newTimerLoop + read -p "Please enter new Timer Loop value in seconds [5-999] (e=Exit): " newTimerLoop if [ -z "$newTimerLoop" ] || echo "$newTimerLoop" | grep -qE "^(e|E)$" then if echo "$timerloop" | grep -qE "^([1-9][0-9]{0,2})$" && \ @@ -3595,15 +3610,41 @@ do [ "$newTimerLoop" -ge 5 ] && [ "$newTimerLoop" -le 999 ] then timerloop="$newTimerLoop" - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: Timer Loop Configuration saved" >> $logfile + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Timer Loop configuration saved" >> $logfile saveconfig timer="$timerloop" printf "\n${CClear}[OK]\n" - sleep 1 ; break + sleep 1 else printf "\n${CRed}*ERROR*: Please enter a valid number between 5 and 999.${CClear}\n" sleep 3 fi + + echo "" + read -p "Please enter new Recovery Timeout value [1-9] (e=Exit): " newRecoveryTimeout + if [ -z "$newRecoveryTimeout" ] || echo "$newRecoveryTimeout" | grep -qE "^(e|E)$" + then + if echo "$recover" | grep -qE "^([1-9])$" && \ + [ "$recover" -ge 1 ] && [ "$recover" -le 9 ] + then + printf "\n${CClear}[Exiting]\n" + sleep 1 ; break + else + printf "\n${CRed}*ERROR*: Please enter a valid number between 1 and 9.${CClear}\n" + sleep 3 + fi + elif echo "$newRecoveryTimeout" | grep -qE "^([1-9])$" && \ + [ "$newRecoveryTimeout" -ge 1 ] && [ "$newRecoveryTimeout" -le 9 ] + then + recover="$newRecoveryTimeout" + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Recovery Timeout Opportunity configuration saved" >> $logfile + saveconfig + printf "\n${CClear}[OK]\n" + sleep 1 ; break + else + printf "\n${CRed}*ERROR*: Please enter a valid number between 1 and 9.${CClear}\n" + sleep 3 + fi done } @@ -3950,6 +3991,7 @@ saveconfig() echo 'PINGHOST="'"$PINGHOST"'"' echo 'logsize='$logsize echo 'timerloop='$timerloop + echo 'recovery='$recovery echo 'schedule='$schedule echo 'schedulehrs='$schedulehrs echo 'schedulemin='$schedulemin @@ -4581,11 +4623,11 @@ restartwg() printf "\33[2K\r" printf "${CGreen}\r[Starting WG Client $1]" service "start_wgc $1" >/dev/null 2>&1 - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WG$1 Connection Restarted - Current Server: $currwghostname | $currwghost" >> $logfile + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WGC$1 Connection Restarted - Current Server: $currwghostname | $currwghost" >> $logfile resettimer $1 "WG" sleep 10 printf "\33[2K\r" - printf "${CGreen}\r[Letting WG$1 Settle]" + printf "${CGreen}\r[Letting WGC$1 Settle]" sleep 10 else #Pick a random server from the alternate servers file, populate in wg client slot, and restart @@ -4616,11 +4658,11 @@ restartwg() printf "\33[2K\r" printf "${CGreen}\r[Starting WG Client $1]" service "start_wgc $1" >/dev/null 2>&1 - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WG$1 Connection Restarted - New Server: $wgdescription | $endpointip" >> $logfile + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WGC$1 Connection Restarted - New Server: $wgdescription | $endpointip" >> $logfile resettimer $1 "WG" sleep 10 printf "\33[2K\r" - printf "${CGreen}\r[Letting WG$1 Settle]" + printf "${CGreen}\r[Letting WGC$1 Settle]" sleep 10 fi else @@ -4629,11 +4671,11 @@ restartwg() printf "${CGreen}\r[Starting WG Client $1]" currwghost="$($timeoutcmd$timeoutsec nvram get wgc$1_ep_addr)" service "start_wgc $1" >/dev/null 2>&1 - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WG$1 Connection Restarted - Current Server: $currwghost" >> $logfile + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WGC$1 Connection Restarted - Current Server: $currwghost" >> $logfile resettimer $1 "WG" sleep 10 printf "\33[2K\r" - printf "${CGreen}\r[Letting WG$1 Settle]" + printf "${CGreen}\r[Letting WGC$1 Settle]" sleep 10 fi @@ -4708,7 +4750,7 @@ killunmonwg() echo "" printf "${CGreen}\r[Stopping WG Client $1]" service "stop_wgc $1" >/dev/null 2>&1 - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WG$1 has been stopped and no longer being monitored" >> $logfile + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: WGC$1 has been stopped and no longer being monitored" >> $logfile sleep 15 printf "\33[2K\r" printf "${CGreen}\r[Unmonitoring WG Client $1]" @@ -5367,8 +5409,19 @@ checkvpn() vpnhealth="${CRed}[FAIL]${CClear}" vpnindicator="${InvRed} ${CClear}" echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: VPN$1 failed to respond" >> $logfile - if [ "$((VPN$1))" = "1" ]; then - resetvpn=$1 + + vrcnt="$((vrcnt+1))" + if [ "$vrcnt" -ge 10 ]; then + monitored="${CRed}[!]${CClear}" + else + monitored="${CRed}[$vrcnt]${CClear}" + fi + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: VPN$1 attempt $vrcnt of $recover to allow connection to recover" >> $logfile + if [ "$rcnt" -eq "$recover" ]; then + if [ "$((VPN$1))" = "1" ]; then + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: VPN$1 failed to respond after $recover attempt(s)" >> $logfile + resetvpn=$1 + fi fi fi fi @@ -5410,17 +5463,28 @@ checkwg() else CNT="$((CNT+1))" printf "\33[2K\r" - printf "\r${InvDkGray} ${CWhite} WG$1${CClear} [Attempt $CNT]" + printf "\r${InvDkGray} ${CWhite} WGC$1${CClear} [Attempt $CNT]" sleep 1 # Giving the VPN a chance to recover a certain number of times - if [ "$CNT" -eq "$TRIES" ];then # But if it fails, report back that we have an issue requiring a VPN reset + if [ "$CNT" -eq "$TRIES" ]; then # But if it fails, report back that we have an issue requiring a VPN reset printf "\33[2K\r" wgping=0 wghealth="${CRed}[FAIL]${CClear}" wgindicator="${InvRed} ${CClear}" - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WG$1 failed to respond" >> $logfile - if [ "$((WG$1))" = "1" ]; then - resetwg=$1 + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WGC$1 failed to respond" >> $logfile + + wrcnt="$((wrcnt+1))" + if [ "$wrcnt" -ge 10 ]; then + wgmonitored="${CRed}[!]${CClear}" + else + wgmonitored="${CRed}[$wrcnt]${CClear}" + fi + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WGC$1 attempt $wrcnt of $recover to allow connection to recover" >> $logfile + if [ "$wrcnt" -eq "$recover" ]; then + if [ "$((WG$1))" = "1" ]; then + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WGC$1 failed to respond after $recover attempt(s)" >> $logfile + resetwg=$1 + fi fi fi fi @@ -5514,7 +5578,7 @@ checkwan() for slot in $availableslots do if [ $((wgstate$slot)) -ne 0 ]; then - printf "\r${InvGreen} ${CClear} [Retrying Kill Command on WG$slot Client Connection]... " + printf "\r${InvGreen} ${CClear} [Retrying Kill Command on WGC$slot Client Connection]... " service "stop_wgc $slot" >/dev/null 2>&1 sleep 3 fi @@ -5871,6 +5935,8 @@ displayopsmenu() fi fi + recoverdisp="${CGreen}R:$recover${CClear}" + #display operations menu if [ "$availableslots" = "1 2" ] then @@ -5879,7 +5945,7 @@ displayopsmenu() echo -e "${InvGreen} ${CClear} Stop/Unmonitor VPN 1:${CGreen}(!)${CClear} 2:${CGreen}(@)${CClear} ${InvGreen} ${CClear} ${CGreen}(R)${CClear}eset VPN/WG CRON Time Scheduler: $schedtime" echo -e "${InvGreen} ${CClear} Enable/Disable ${CGreen}(M)${CClear}onitored VPN Slots ${InvGreen} ${CClear} ${CGreen}(L)${CClear}og Viewer / Trim Log Size (rows): $logSizeStr" echo -e "${InvGreen} ${CClear} Update/Maintain ${CGreen}(V)${CClear}PN Server Lists ${InvGreen} ${CClear} ${CGreen}(A)${CClear}utostart VPNMON-R3 on Reboot: $rebootprot" - echo -e "${InvGreen} ${CClear} Edit/R${CGreen}(U)${CClear}n Server List Automation ${InvGreen} ${CClear} ${CGreen}(T)${CClear}imer VPN Check Loop Interval: $timerLoopStr" + echo -e "${InvGreen} ${CClear} Edit/R${CGreen}(U)${CClear}n Server List Automation ${InvGreen} ${CClear} ${CGreen}(T)${CClear}imer Loop Check Interval: $timerLoopStr | $recoverdisp" echo -e "${InvGreen} ${CClear} AMTM Email Not${CGreen}(I)${CClear}fications: $amtmdisp ${InvGreen} ${CClear} ${CGreen}(P)${CClear}ing Maximum Before Reset in ms: $pingResetStr" echo -e "${InvGreen} ${CClear}${CDkGray}--------------------------------------------------------------------------------------------------------------${CClear}" echo "" @@ -5890,7 +5956,7 @@ displayopsmenu() echo -e "${InvGreen} ${CClear} Stop/Unmonitor VPN 1:${CGreen}(!)${CClear} 2:${CGreen}(@)${CClear} 3:${CGreen}(#)${CClear} 4:${CGreen}($)${CClear} 5:${CGreen}(%)${CClear} ${InvGreen} ${CClear} ${CGreen}(R)${CClear}eset VPN/WG CRON Time Scheduler: $schedtime" echo -e "${InvGreen} ${CClear} Reset/Reconnect WG 1:${CGreen}(6)${CClear} 2:${CGreen}(7)${CClear} 3:${CGreen}(8)${CClear} 4:${CGreen}(9)${CClear} 5:${CGreen}(0)${CClear} ${InvGreen} ${CClear} ${CGreen}(L)${CClear}og Viewer / Trim Log Size (rows): $logSizeStr" echo -e "${InvGreen} ${CClear} Stop/Unmonitor WG 1:${CGreen}(^)${CClear} 2:${CGreen}(&)${CClear} 3:${CGreen}(-)${CClear} 4:${CGreen}(+)${CClear} 5:${CGreen}(=)${CClear} ${InvGreen} ${CClear} ${CGreen}(A)${CClear}utostart VPNMON-R3 on Reboot: $rebootprot" - echo -e "${InvGreen} ${CClear} Enable/Disable ${CGreen}(M)${CClear}onitored VPN/WG Slots ${InvGreen} ${CClear} ${CGreen}(T)${CClear}imer VPN Check Loop Interval: $timerLoopStr" + echo -e "${InvGreen} ${CClear} Enable/Disable ${CGreen}(M)${CClear}onitored VPN/WG Slots ${InvGreen} ${CClear} ${CGreen}(T)${CClear}imer Loop Check Interval: $timerLoopStr | $recoverdisp" echo -e "${InvGreen} ${CClear} Update/Maintain ${CGreen}(V)${CClear}PN/${CGreen}(W)${CClear}G Server Lists ${InvGreen} ${CClear} ${CGreen}(P)${CClear}ing Maximum Before Reset in ms: $pingResetStr" echo -e "${InvGreen} ${CClear} Edit/R${CGreen}(U)${CClear}n Server List Automation ${InvGreen} ${CClear} AMTM Email Not${CGreen}(I)${CClear}fications: $amtmdisp" echo -e "${InvGreen} ${CClear}${CDkGray}--------------------------------------------------------------------------------------------------------------${CClear}" @@ -6525,7 +6591,7 @@ do #if a wg connection is monitored and disconnected, try to restart it if [ "$((WG$i))" = "1" ] && [ "$wgstate" = "Disconnected" ] then #reconnect - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WG$i has disconnected" >> $logfile + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WGC$i has disconnected" >> $logfile echo "" printf "\33[2K\r" @@ -6556,7 +6622,7 @@ do idle_seconds=$((`date +%s`-${last_handshake})) if [ "$idle_seconds" -gt "200" ] then #reconnect - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WG$i handshake exceeded 200s" >> $logfile + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WGC$i handshake exceeded 200s" >> $logfile echo "" printf "\33[2K\r" @@ -6593,7 +6659,7 @@ do then echo "" printf "\33[2K\r" - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WG$i PING exceeds max allowed ($pingreset ms)" >> $logfile + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WGC$i PING exceeds max allowed ($pingreset ms)" >> $logfile printf "${CGreen}\r[Maximum PING Exceeded]" sleep 3 printf "\33[2K\r" @@ -6620,7 +6686,7 @@ do #if a wg is monitored and not responsive, try to restart it if [ "$((WG$i))" = "1" ] && [ "$resetwg" != "0" ] then #reconnect - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WG$i is non-responsive and being reconnected" >> $logfile + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WGC$i is non-responsive and being reconnected" >> $logfile echo "" printf "\33[2K\r" @@ -6714,7 +6780,7 @@ do if [ "$usewg" = "1" ] then printf "\33[2K\r" - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WG$unboundreset is out of sync with Unbound DNS Resolver" >> $logfile + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: WGC$unboundreset is out of sync with Unbound DNS Resolver" >> $logfile printf "${CGreen}\r[Unbound is out of sync]" sleep 3 printf "\33[2K\r" From c88ce5ef8d794c48f90afd604545864b658610e4 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Thu, 14 Aug 2025 01:09:10 +0000 Subject: [PATCH 22/48] Update enhancements.txt --- enhancements.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/enhancements.txt b/enhancements.txt index 8ba1562..72c68e8 100644 --- a/enhancements.txt +++ b/enhancements.txt @@ -1,12 +1,10 @@ To-Do: - If the WAN doesn't respond after X minutes, issue a router reboot? -- Specify how many times you want cycle to go before a reset occurs (right now it's every cycle, but could make it 2 or 3?) - Done: - Remove RL tag if amtm email functionality is turned off completely, and show some info in the config menu on the AMTM email item. - Get unbound over WG working - +- Specify how many times you want cycle to go before a reset occurs (right now it's every cycle, but could make it 2 or 3?) - Option to show/hide OVPN and WG sections. @Stephen Harrington - Wireguard compatibility - *** if downloaded list = 0, then dont save list and preserve, and email From 7a2b202a6b8c9722f2f0339715ca6f58c2156ea1 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Thu, 14 Aug 2025 01:12:23 +0000 Subject: [PATCH 23/48] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1279ad3..afe63c3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# VPNMON-R3 v1.6.0 Beta 2 +# VPNMON-R3 v1.6.0 Beta 3 Asus-Merlin OpenVPN/Wireguard Monitor R3 -Updated on 2025-Aug-12 +Updated on 2025-Aug-13 --- From ccf0dec8cbf602ceb192d3690f22a24cfb394495 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Thu, 14 Aug 2025 01:21:08 +0000 Subject: [PATCH 24/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index b0658cc..b8ad1bb 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -3582,16 +3582,16 @@ do echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Recovery Timeout Opportunities are simply the number of chances the script will${CClear}" echo -e "${InvGreen} ${CClear} give the connection to recover on its own before intervening. With this value${CClear}" - echo -e "${InvGreen} ${CClear} defaulting to 1, if connectivity issues occur within the default 60 second Timer${CClear}" + echo -e "${InvGreen} ${CClear} defaulting to 1x, if connectivity issues occur within the default 60 second Timer${CClear}" echo -e "${InvGreen} ${CClear} Loop, the connectivity will be reset after those 60 seconds." echo -e "${InvGreen} ${CClear}" - echo -e "${InvGreen} ${CClear} Example: 60 Second Timer Loops with 3 Recovery Timeouts would reset the connection${CClear}" + echo -e "${InvGreen} ${CClear} Example: 60 Second Timer Loops with 3x Recovery Timeouts would reset the connection${CClear}" echo -e "${InvGreen} ${CClear} after 3 minutes." echo -e "${InvGreen} ${CClear}" - echo -e "${InvGreen} ${CClear} (Defaults: Timer Loop = 60 seconds | Recovery Timeouts = 1)${CClear}" + echo -e "${InvGreen} ${CClear} (Defaults: Timer Loop = 60 seconds | Recovery Timeouts = 1x)${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" - echo -e "${InvGreen} ${CClear} Current: Timer Loop = ${CGreen}$timerloop sec ${CClear}| Recovery Timeouts: ${CGreen}$recover${CClear}" + echo -e "${InvGreen} ${CClear} Current: Timer Loop = ${CGreen}$timerloop sec ${CClear}| Recovery Timeouts: ${CGreen}${recover}x${CClear}" echo read -p "Please enter new Timer Loop value in seconds [5-999] (e=Exit): " newTimerLoop if [ -z "$newTimerLoop" ] || echo "$newTimerLoop" | grep -qE "^(e|E)$" @@ -5935,7 +5935,7 @@ displayopsmenu() fi fi - recoverdisp="${CGreen}R:$recover${CClear}" + recoverdisp="${CGreen}Recovery:${recover}x${CClear}" #display operations menu if [ "$availableslots" = "1 2" ] From 667da545c2dbdf3fe380668ef787309b8ae0fd2d Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Thu, 14 Aug 2025 01:21:32 +0000 Subject: [PATCH 25/48] Update version.txt --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index b13a760..0670af1 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.6.0b1 +1.6.0b3 From 445e46cbd4ae6137b6a4d460f0421674261081d4 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Thu, 14 Aug 2025 01:24:11 +0000 Subject: [PATCH 26/48] Update changelog.txt --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index f7abdb1..c43443b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -18,7 +18,7 @@ v1.6.0b3 - (TBA) example, your timer loop = 60 seconds. Your Recovery Timeout is set to 3x. This would mean if your connection doesn't recover in 3 minutes, then it will issue a reset. This option might help bring down the amount of times your connection resets throughout the day, but then there's also a greater - chance your connection might become unusable for a slighly longer amount of time. + chance your connection might become unusable for a slightly longer amount of time. v1.5.0 - (August 3, 2025) - MINOR: Well, actually kinda major. Wireguard is now formally being supported! VPNMON-R3 will show all From 0d7f598ce500749d7928ece07d90ce1a90c854c4 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Thu, 14 Aug 2025 01:34:55 +0000 Subject: [PATCH 27/48] Update changelog.txt --- changelog.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index c43443b..a66ab6b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -18,7 +18,8 @@ v1.6.0b3 - (TBA) example, your timer loop = 60 seconds. Your Recovery Timeout is set to 3x. This would mean if your connection doesn't recover in 3 minutes, then it will issue a reset. This option might help bring down the amount of times your connection resets throughout the day, but then there's also a greater - chance your connection might become unusable for a slightly longer amount of time. + chance your connection might become unusable for a slightly longer amount of time. This was inspired + by @CaptainSTX! Thank you! v1.5.0 - (August 3, 2025) - MINOR: Well, actually kinda major. Wireguard is now formally being supported! VPNMON-R3 will show all From caca0750d6080f3ef79fed9e65d5ee48fc190dbc Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:08:31 +0000 Subject: [PATCH 28/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index b8ad1bb..86bb1cb 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -7,7 +7,7 @@ # are connected, and sends a ping to a host of your choice through each active connection. If it finds that a connection # has been lost, it will execute a series of commands that will kill that single VPN client, and randomly picks one of # your specified servers to reconnect to for each VPN client. -# Last Modified: 2025-Aug-13 +# Last Modified: 2025-Aug-14 ########################################################################################## #Preferred standard router binaries path @@ -3593,21 +3593,21 @@ do echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} Current: Timer Loop = ${CGreen}$timerloop sec ${CClear}| Recovery Timeouts: ${CGreen}${recover}x${CClear}" echo - read -p "Please enter new Timer Loop value in seconds [5-999] (e=Exit): " newTimerLoop + read -p "Please enter new Timer Loop value in seconds [10-999] (e=Exit): " newTimerLoop if [ -z "$newTimerLoop" ] || echo "$newTimerLoop" | grep -qE "^(e|E)$" then if echo "$timerloop" | grep -qE "^([1-9][0-9]{0,2})$" && \ - [ "$timerloop" -ge 5 ] && [ "$timerloop" -le 999 ] + [ "$timerloop" -ge 10 ] && [ "$timerloop" -le 999 ] then timer="$timerloop" printf "\n${CClear}[Exiting]\n" sleep 1 ; break else - printf "\n${CRed}*ERROR*: Please enter a valid number between 5 and 999.${CClear}\n" + printf "\n${CRed}*ERROR*: Please enter a valid number between 10 and 999.${CClear}\n" sleep 3 fi elif echo "$newTimerLoop" | grep -qE "^([1-9][0-9]{0,2})$" && \ - [ "$newTimerLoop" -ge 5 ] && [ "$newTimerLoop" -le 999 ] + [ "$newTimerLoop" -ge 10 ] && [ "$newTimerLoop" -le 999 ] then timerloop="$newTimerLoop" echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - INFO: New Timer Loop configuration saved" >> $logfile @@ -3616,7 +3616,7 @@ do printf "\n${CClear}[OK]\n" sleep 1 else - printf "\n${CRed}*ERROR*: Please enter a valid number between 5 and 999.${CClear}\n" + printf "\n${CRed}*ERROR*: Please enter a valid number between 10 and 999.${CClear}\n" sleep 3 fi @@ -5935,7 +5935,7 @@ displayopsmenu() fi fi - recoverdisp="${CGreen}Recovery:${recover}x${CClear}" + recoverdisp="${CClear}Recovery: ${CGreen}${recover}x${CClear}" #display operations menu if [ "$availableslots" = "1 2" ] From 9b103c4ff205e088810e96d3339609de1a3ab2a7 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Thu, 14 Aug 2025 11:08:41 +0000 Subject: [PATCH 29/48] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index afe63c3..4c86c5e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # VPNMON-R3 v1.6.0 Beta 3 Asus-Merlin OpenVPN/Wireguard Monitor R3 -Updated on 2025-Aug-13 +Updated on 2025-Aug-14 --- From 8be4337c9becdfadbcbe873b5fbb0cfba17a7159 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Thu, 14 Aug 2025 22:04:36 +0000 Subject: [PATCH 30/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index 86bb1cb..efbfbc4 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -5390,6 +5390,7 @@ checkvpn() if [ "$VP" -eq 0 ]; then vpnhealth="${CGreen}[ OK ]${CClear}" vpnindicator="${InvGreen} ${CClear}" + vrcnt=0 else vpnping=0 vpnhealth="${CYellow}[UNKN]${CClear}" @@ -5417,7 +5418,7 @@ checkvpn() monitored="${CRed}[$vrcnt]${CClear}" fi echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: VPN$1 attempt $vrcnt of $recover to allow connection to recover" >> $logfile - if [ "$rcnt" -eq "$recover" ]; then + if [ "$vrcnt" -eq "$recover" ]; then if [ "$((VPN$1))" = "1" ]; then echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: VPN$1 failed to respond after $recover attempt(s)" >> $logfile resetvpn=$1 @@ -5453,6 +5454,7 @@ checkwg() if [ "$VP" -eq 0 ]; then wghealth="${CGreen}[ OK ]${CClear}" wgindicator="${InvGreen} ${CClear}" + wrcnt=0 else wgping=0 wghealth="${CYellow}[UNKN]${CClear}" From e15a9b1946e6e64db13b8b4faffa5eb4c1047494 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Fri, 15 Aug 2025 00:47:37 +0000 Subject: [PATCH 31/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index efbfbc4..20cd997 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -5933,7 +5933,7 @@ displayopsmenu() if [ "$ratelimit" = "0" ]; then rldisp="| ${CRed}RL" else - rldisp="| ${CGreen}RL:$ratelimit/h" + rldisp="| RL: ${CGreen}$ratelimit/h" fi fi From 5da0588c2595892fb20d64ea9dcf343a5895783d Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Fri, 15 Aug 2025 15:34:49 -0400 Subject: [PATCH 32/48] Update enhancements.txt --- enhancements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/enhancements.txt b/enhancements.txt index 72c68e8..92cd123 100644 --- a/enhancements.txt +++ b/enhancements.txt @@ -1,5 +1,7 @@ To-Do: - If the WAN doesn't respond after X minutes, issue a router reboot? +- Invalid ping response values in log +- "Time Connected" in UI Done: - Remove RL tag if amtm email functionality is turned off completely, and show some info in the config menu on the AMTM email item. From fe0d36b04056bd4982624d7a9894fdc4b683d337 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Fri, 15 Aug 2025 15:39:11 -0400 Subject: [PATCH 33/48] Update enhancements.txt --- enhancements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/enhancements.txt b/enhancements.txt index 92cd123..9d821e3 100644 --- a/enhancements.txt +++ b/enhancements.txt @@ -2,6 +2,7 @@ To-Do: - If the WAN doesn't respond after X minutes, issue a router reboot? - Invalid ping response values in log - "Time Connected" in UI +- Router Uptime on WAN entry in UI Done: - Remove RL tag if amtm email functionality is turned off completely, and show some info in the config menu on the AMTM email item. From 2a6892e3c708adabc5553484ffeca93c0dfc24a8 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Sat, 16 Aug 2025 09:58:37 +0000 Subject: [PATCH 34/48] Update enhancements.txt --- enhancements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/enhancements.txt b/enhancements.txt index 9d821e3..7a1937f 100644 --- a/enhancements.txt +++ b/enhancements.txt @@ -1,10 +1,10 @@ To-Do: - If the WAN doesn't respond after X minutes, issue a router reboot? + +Done: - Invalid ping response values in log - "Time Connected" in UI - Router Uptime on WAN entry in UI - -Done: - Remove RL tag if amtm email functionality is turned off completely, and show some info in the config menu on the AMTM email item. - Get unbound over WG working - Specify how many times you want cycle to go before a reset occurs (right now it's every cycle, but could make it 2 or 3?) From a57a5da383e3642a189b4c1a01605779e38bbdb9 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Sat, 16 Aug 2025 10:09:49 +0000 Subject: [PATCH 35/48] Update changelog.txt --- changelog.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index a66ab6b..58d2c6b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,4 @@ -v1.6.0b3 - (TBA) +v1.6.0b4 - (TBA) - MINOR: Very happy to announce that "Unbound-over-Wireguard" has actually become a reality! With huge help and guidance from @ZebMcKayhan, a methodology was developed that is easier, simpler and much more elegant than it's Unbound-over-VPN twin. In essence, VPNMON-R3 creates a new virtual br0 interface, @@ -20,6 +20,9 @@ v1.6.0b3 - (TBA) down the amount of times your connection resets throughout the day, but then there's also a greater chance your connection might become unusable for a slightly longer amount of time. This was inspired by @CaptainSTX! Thank you! + - PATCH: Added the router uptime value under the WAN monitoring section, some visual enhancements to + the main UI, and providing content values in the logs when you get "invalid ping response received" + messages, showing exactly what it was coming back with. v1.5.0 - (August 3, 2025) - MINOR: Well, actually kinda major. Wireguard is now formally being supported! VPNMON-R3 will show all From 5d73a8791b39037feaffaa52f5ffc62cdfa66093 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Sat, 16 Aug 2025 10:11:00 +0000 Subject: [PATCH 36/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index 20cd997..f428cfd 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -1,6 +1,6 @@ #!/bin/sh -# VPNMON-R3 v1.6.0b3 (VPNMON-R3.SH) is an all-in-one script that is optimized to maintain multiple VPN connections and is +# VPNMON-R3 v1.6.0b4 (VPNMON-R3.SH) is an all-in-one script that is optimized to maintain multiple VPN connections and is # able to provide for the capabilities to randomly reconnect using a specified server list containing the servers of your # choice. Special care has been taken to ensure that only the VPN connections you want to have monitored are tended to. # This script will check the health of up to 5 VPN connections on a regular interval to see if monitored VPN conenctions @@ -14,7 +14,7 @@ export PATH="/sbin:/bin:/usr/sbin:/usr/bin:$PATH" #Static Variables - please do not change -version="1.6.0b3" # Version tracker +version="1.6.0b4" # Version tracker beta=1 # Beta switch screenshotmode=0 # Switch to present bogus info for screenshots apppath="/jffs/scripts/vpnmon-r3.sh" # Static path to the app @@ -5642,6 +5642,9 @@ wancheck() WANIFNAME="$(get_wan_setting ifname)" DUALWANMODE="$($timeoutcmd$timeoutsec nvram get wans_mode)" + # Uptime calc # + uptimeStr="$(awk '{printf("%dd %02dh:%02dm\n",($1/60/60/24),($1/60/60%24),($1/60%60),($1%60))}' /proc/uptime)" + # If WAN 0 or 1 is connected, then proceed, else display that it's inactive if [ "$WANIF" = "0" ] then @@ -5684,11 +5687,11 @@ wancheck() then echo -en "${InvGreen} ${InvDkGray}${CWhite} WAN0${CClear} | ${CGreen}[X]${CClear} | " printf "%-6s" "$WAN0IFNAME" - echo -e " | ${CGreen}[ OK ]${CClear} | Failover | $WAN0IP | $WAN0PING | $WAN0CITY" + echo -e " | ${CGreen}[ OK ]${CClear} | Failover | $WAN0IP | $WAN0PING | $WAN0CITY: $uptimeStr" else echo -en "${InvGreen} ${InvDkGray}${CWhite} WAN0${CClear} | ${CGreen}[X]${CClear} | " printf "%-6s" "$WAN0IFNAME" - echo -e " | ${CGreen}[ OK ]${CClear} | Active | $WAN0IP | $WAN0PING | $WAN0CITY" + echo -e " | ${CGreen}[ OK ]${CClear} | Active | $WAN0IP | $WAN0PING | $WAN0CITY: $uptimeStr" fi else echo -e "${InvDkGray}${CWhite} WAN0${CClear} | ${CGreen}[X]${CClear} | ${CDkGray}[n/a]${CClear} | ${CDkGray}[n/a ]${CClear} | Inactive | ${CDkGray}[n/a]${CClear} | ${CDkGray}[n/a]${CClear} | ${CDkGray}[n/a]${CClear}" @@ -5729,11 +5732,11 @@ wancheck() then echo -en "${InvGreen} ${InvDkGray}${CWhite} WAN1${CClear} | ${CGreen}[X]${CClear} | " printf "%-6s" "$WAN1IFNAME" - echo -e " | ${CGreen}[ OK ]${CClear} | Failover | $WAN1IP | $WAN1PING | $WAN1CITY" + echo -e " | ${CGreen}[ OK ]${CClear} | Failover | $WAN1IP | $WAN1PING | $WAN1CITY: $uptimeStr" else echo -en "${InvGreen} ${InvDkGray}${CWhite} WAN1${CClear} | ${CGreen}[X]${CClear} | " printf "%-6s" "$WAN1IFNAME" - echo -e " | ${CGreen}[ OK ]${CClear} | Active | $WAN1IP | $WAN1PING | $WAN1CITY" + echo -e " | ${CGreen}[ OK ]${CClear} | Active | $WAN1IP | $WAN1PING | $WAN1CITY: $uptimeStr" fi else echo -e "${InvDkGray}${CWhite} WAN1${CClear} | ${CGreen}[X]${CClear} | ${CDkGray}[n/a]${CClear} | ${CDkGray}[n/a ]${CClear} | Inactive | ${CDkGray}[n/a]${CClear} | ${CDkGray}[n/a]${CClear} | ${CDkGray}[n/a]${CClear}" @@ -6258,7 +6261,7 @@ do if [ "$monitorwan" = "1" ] && [ "$firstrun" = "0" ] then #Display WAN ports grid - echo -e "${CClear} Port | Mon | IFace | Health | WAN State | Public WAN IP | Ping-->WAN | City Exit" + echo -e "${CClear} Port | Mon | IFace | Health | WAN State | Public WAN IP | Ping-->WAN | City Exit / Uptime" echo -e "-------|-----|--------|--------|--------------|-----------------|------------|---------------------------------" #Cycle through the WANCheck connection function to display ping/city info @@ -6277,9 +6280,9 @@ do echo "" #Display VPN client slot grid if [ "$unboundclient" != "0" ]; then - echo -e " Slot | Mon | Svrs | Health | VPN State | Public VPN IP | Ping-->VPN | City Exit / Time / UB" + echo -e " Slot | Mon | Svrs | Health | VPN State | Public VPN IP | Ping-->VPN | City Exit / Time Connected / UB" else - echo -e " Slot | Mon | Svrs | Health | VPN State | Public VPN IP | Ping-->VPN | City Exit / Time" + echo -e " Slot | Mon | Svrs | Health | VPN State | Public VPN IP | Ping-->VPN | City Exit / Time Connected" fi echo -e "-------|-----|--------|--------|--------------|-----------------|------------|---------------------------------" @@ -6457,8 +6460,11 @@ do maxsvrping=$(awk "BEGIN {printf \"%3.0f\", ${vpnping}}") >/dev/null 2>&1 MP=$? if [ $MP -ne 0 ]; then + if [ -z "$MP" ] || [ "$MP" = "" ]; then + maxsvrping="Null" + fi + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: Invalid VPN PING information received. Contents: $maxsvrping" >> $logfile maxsvrping=0 - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: Invalid VPN PING information received." >> $logfile fi if [ "$pingreset" -gt 0 ] @@ -6509,7 +6515,12 @@ do echo "" #Display WG client slot grid - echo -e " Slot | Mon | Svrs | Health | WG State | Public WG IP | Ping--->WG | City Exit / Time" + if [ "$unboundwgclient" != "0" ]; then + echo -e " Slot | Mon | Svrs | Health | WG State | Public WG IP | Ping--->WG | City Exit / Time Connected / UB" + else + echo -e " Slot | Mon | Svrs | Health | WG State | Public WG IP | Ping--->WG | City Exit / Time Connected" + fi + echo -e "-------|-----|--------|--------|--------------|-----------------|------------|---------------------------------" i=0 @@ -6651,8 +6662,11 @@ do maxsvrping=$(awk "BEGIN {printf \"%3.0f\", ${wgping}}") >/dev/null 2>&1 MP=$? if [ $MP -ne 0 ]; then + if [ -z "$MP" ] || [ "$MP" = "" ]; then + maxsvrping="Null" + fi + echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: Invalid WG PING information received. Contents: $maxsvrping" >> $logfile maxsvrping=0 - echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: Invalid WG PING information received." >> $logfile fi if [ "$pingreset" -gt 0 ] From 4d07545a3dd7d23ea96fc909eeeff39f5752e63f Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Sat, 16 Aug 2025 10:11:11 +0000 Subject: [PATCH 37/48] Update version.txt --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 0670af1..80f945a 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.6.0b3 +1.6.0b4 From c4262489d2a919c139cef0d39e76440abfbe1647 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Sat, 16 Aug 2025 16:37:31 +0000 Subject: [PATCH 38/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index f428cfd..2d2dcbb 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -7,7 +7,7 @@ # are connected, and sends a ping to a host of your choice through each active connection. If it finds that a connection # has been lost, it will execute a series of commands that will kill that single VPN client, and randomly picks one of # your specified servers to reconnect to for each VPN client. -# Last Modified: 2025-Aug-14 +# Last Modified: 2025-Aug-16 ########################################################################################## #Preferred standard router binaries path @@ -6460,8 +6460,8 @@ do maxsvrping=$(awk "BEGIN {printf \"%3.0f\", ${vpnping}}") >/dev/null 2>&1 MP=$? if [ $MP -ne 0 ]; then - if [ -z "$MP" ] || [ "$MP" = "" ]; then - maxsvrping="Null" + if [ -z "$maxsvrping" ] || [ "$maxsvrping" = "" ]; then + maxsvrping="Null" fi echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: Invalid VPN PING information received. Contents: $maxsvrping" >> $logfile maxsvrping=0 @@ -6662,8 +6662,8 @@ do maxsvrping=$(awk "BEGIN {printf \"%3.0f\", ${wgping}}") >/dev/null 2>&1 MP=$? if [ $MP -ne 0 ]; then - if [ -z "$MP" ] || [ "$MP" = "" ]; then - maxsvrping="Null" + if [ -z "$maxsvrping" ] || [ "$maxsvrping" = "" ]; then + maxsvrping="Null" fi echo -e "$(date +'%b %d %Y %X') $(_GetLAN_HostName_) VPNMON-R3[$$] - WARNING: Invalid WG PING information received. Contents: $maxsvrping" >> $logfile maxsvrping=0 From 4c51f4ed12cc2c4aa2fc496794dcd59daac46a6e Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Sat, 16 Aug 2025 16:37:49 +0000 Subject: [PATCH 39/48] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4c86c5e..a2a5a8d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# VPNMON-R3 v1.6.0 Beta 3 +# VPNMON-R3 v1.6.0 Beta 4 Asus-Merlin OpenVPN/Wireguard Monitor R3 -Updated on 2025-Aug-14 +Updated on 2025-Aug-16 --- From 415a68da8842490e7940c7fe7a38c3dad959bbd8 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Sat, 16 Aug 2025 20:09:09 +0000 Subject: [PATCH 40/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index 2d2dcbb..6dd38b7 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -15,7 +15,7 @@ export PATH="/sbin:/bin:/usr/sbin:/usr/bin:$PATH" #Static Variables - please do not change version="1.6.0b4" # Version tracker -beta=1 # Beta switch +beta=0 # Beta switch screenshotmode=0 # Switch to present bogus info for screenshots apppath="/jffs/scripts/vpnmon-r3.sh" # Static path to the app logfile="/jffs/addons/vpnmon-r3.d/vpnmon-r3.log" # Static path to the log @@ -5113,6 +5113,7 @@ getvpnip() # Find the WG IP getwgip() { + ubsync="" TUN="wgc$1" # Added ping workaround for site2site scenarios based on suggestion from @ZebMcKayhan From 4787152e058f0bfabe4801c6163e8b3efe94a8b3 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Sat, 16 Aug 2025 20:10:01 +0000 Subject: [PATCH 41/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index 6dd38b7..0529e0f 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -15,7 +15,7 @@ export PATH="/sbin:/bin:/usr/sbin:/usr/bin:$PATH" #Static Variables - please do not change version="1.6.0b4" # Version tracker -beta=0 # Beta switch +beta=1 # Beta switch screenshotmode=0 # Switch to present bogus info for screenshots apppath="/jffs/scripts/vpnmon-r3.sh" # Static path to the app logfile="/jffs/addons/vpnmon-r3.d/vpnmon-r3.log" # Static path to the log From 4314983153a8e78f1b8a29c9974198f768febac4 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Thu, 21 Aug 2025 00:51:00 +0000 Subject: [PATCH 42/48] Update changelog.txt --- changelog.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 58d2c6b..721e86f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,4 @@ -v1.6.0b4 - (TBA) +v1.6.0b5 - (TBA) - MINOR: Very happy to announce that "Unbound-over-Wireguard" has actually become a reality! With huge help and guidance from @ZebMcKayhan, a methodology was developed that is easier, simpler and much more elegant than it's Unbound-over-VPN twin. In essence, VPNMON-R3 creates a new virtual br0 interface, @@ -23,6 +23,11 @@ v1.6.0b4 - (TBA) - PATCH: Added the router uptime value under the WAN monitoring section, some visual enhancements to the main UI, and providing content values in the logs when you get "invalid ping response received" messages, showing exactly what it was coming back with. + - PATCH: Added compatibility for AirVPN WG, which not only requires a Public Key, a Private Key, but + also a Preshared Key. A 6th field is now an optional requirement for AirVPN or vpn providers like + them who require this field. Both the Update/Maintain WG server lists and Run Server List + Automation screens have updated language, while code in charge of resetting the WG connection will + insert this Preshared Key into the appropriate NVRAM value for the WGC slot. v1.5.0 - (August 3, 2025) - MINOR: Well, actually kinda major. Wireguard is now formally being supported! VPNMON-R3 will show all From bbaa443f981147227d6924c3a0b5a00a546724c9 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Thu, 21 Aug 2025 00:51:48 +0000 Subject: [PATCH 43/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index 0529e0f..31f6bfa 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -1,20 +1,20 @@ #!/bin/sh -# VPNMON-R3 v1.6.0b4 (VPNMON-R3.SH) is an all-in-one script that is optimized to maintain multiple VPN connections and is +# VPNMON-R3 v1.6.0b5 (VPNMON-R3.SH) is an all-in-one script that is optimized to maintain multiple VPN connections and is # able to provide for the capabilities to randomly reconnect using a specified server list containing the servers of your # choice. Special care has been taken to ensure that only the VPN connections you want to have monitored are tended to. # This script will check the health of up to 5 VPN connections on a regular interval to see if monitored VPN conenctions # are connected, and sends a ping to a host of your choice through each active connection. If it finds that a connection # has been lost, it will execute a series of commands that will kill that single VPN client, and randomly picks one of # your specified servers to reconnect to for each VPN client. -# Last Modified: 2025-Aug-16 +# Last Modified: 2025-Aug-20 ########################################################################################## #Preferred standard router binaries path export PATH="/sbin:/bin:/usr/sbin:/usr/bin:$PATH" #Static Variables - please do not change -version="1.6.0b4" # Version tracker +version="1.6.0b5" # Version tracker beta=1 # Beta switch screenshotmode=0 # Switch to present bogus info for screenshots apppath="/jffs/scripts/vpnmon-r3.sh" # Static path to the app @@ -2383,9 +2383,9 @@ do echo -e "${InvGreen} ${CClear} Please indicate which WG Slot Server List you would like to edit/maintain.${CClear}" echo -e "${InvGreen} ${CClear} Use the corresponding ${CGreen}()${CClear} key to launch and edit the list with the NANO text editor${CClear}" echo -e "${InvGreen} ${CClear}" - echo -e "${InvGreen} ${CClear} WG INSTRUCTIONS: Enter a 5-field comma-delimited row of WG Connections${CClear}" - echo -e "${InvGreen} ${CClear} Format: ConnectionName,EndpointIP,EndpointPort,PrivateKey,PublicKey${CClear}" - echo -e "${InvGreen} ${CClear} Example: ${CGreen}City WG,143.32.55.23,34334,fasdkkfj44j38affkasdjfj=,221t949asas42dfj32323kf=${CClear}" + echo -e "${InvGreen} ${CClear} WG INSTRUCTIONS: Enter a 6-field comma-delimited row of WG Connections${CClear}" + echo -e "${InvGreen} ${CClear} Format: ConnectionName,EndpointIP,EndpointPort,PrivateKey,PublicKey,PreSharedKey(Opt)${CClear}" + echo -e "${InvGreen} ${CClear} Example: ${CGreen}City WG,143.32.55.23,34334,fasdkaffkasdjfj=,221t949as2323kf=,23fj39fffjdaf=${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} NANO INSTRUCTIONS: CTRL-O + Enter (save), CTRL-X (exit)${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" @@ -2466,8 +2466,9 @@ do echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} VPN INSTRUCTIONS: Insert CURL statement that outputs a single-column Server IP list${CClear}" echo -e "${InvGreen} ${CClear}" - echo -e "${InvGreen} ${CClear} WG INSTRUCTIONS: Insert CURL statement that outputs a 5-field comma-separated list${CClear}" - echo -e "${InvGreen} ${CClear} in this format: ConnectionName,EndpointIP,EndpointPort,PrivateKey,PublicKey${CClear}" + echo -e "${InvGreen} ${CClear} WG INSTRUCTIONS: Insert CURL statement that outputs a 6-field comma-separated list${CClear}" + echo -e "${InvGreen} ${CClear} Format: ConnectionName,EndpointIP,EndpointPort,PrivateKey,PublicKey,PreSharedKey(Opt)${CClear}" + echo -e "${InvGreen} ${CClear} Example: ${CGreen}City WG,143.32.55.23,34334,fasdkaffkasdjfj=,221t949as2323kf=,23fj39fffjdaf=${CClear}" echo -e "${InvGreen} ${CClear}${CDkGray}---------------------------------------------------------------------------------------${CClear}" echo -e "${InvGreen} ${CClear}" echo -e "${InvGreen} ${CClear} ${InvDkGray}${CWhite}VPN1${CClear} ${CGreen}(e1)${CClear} View/Edit | ${CGreen}(x1)${CClear} Execute | ${CGreen}(s1)${CClear} Skynet WL Import${CClear}" @@ -4644,12 +4645,14 @@ restartwg() endpointport=$(echo "$WGLINE" | cut -d ',' -f 3) privatekey=$(echo "$WGLINE" | cut -d ',' -f 4) publickey=$(echo "$WGLINE" | cut -d ',' -f 5) + presharedkey=$(echo "$WGLINE" | cut -d ',' -f 6) #Optional, required by AirVPN nvram set wgc"$1"_desc="$wgdescription" nvram set wgc"$1"_ep_addr="$endpointip" nvram set wgc"$1"_ep_addr_r="$endpointip" nvram set wgc"$1"_ep_port="$endpointport" nvram set wgc"$1"_priv="${privatekey}" nvram set wgc"$1"_ppub="${publickey}" + nvram set wgc"$1"_psk="${presharedkey}" #Optional, required by AirVPN sleep 2 #---------WG-specific nvram values From 976b787597c069ff4cd9f780c0dc835e748d22e1 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Thu, 21 Aug 2025 00:52:04 +0000 Subject: [PATCH 44/48] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a2a5a8d..efddbb2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# VPNMON-R3 v1.6.0 Beta 4 +# VPNMON-R3 v1.6.0 Beta 5 Asus-Merlin OpenVPN/Wireguard Monitor R3 -Updated on 2025-Aug-16 +Updated on 2025-Aug-20 --- From 460e083c86e8c8ca297746061a5e3b7036704cb7 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Fri, 22 Aug 2025 00:11:03 +0000 Subject: [PATCH 45/48] Update vpnmon-r3.sh --- vpnmon-r3.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vpnmon-r3.sh b/vpnmon-r3.sh index 31f6bfa..33ef4eb 100755 --- a/vpnmon-r3.sh +++ b/vpnmon-r3.sh @@ -1,21 +1,21 @@ #!/bin/sh -# VPNMON-R3 v1.6.0b5 (VPNMON-R3.SH) is an all-in-one script that is optimized to maintain multiple VPN connections and is +# VPNMON-R3 v1.6.0 (VPNMON-R3.SH) is an all-in-one script that is optimized to maintain multiple VPN connections and is # able to provide for the capabilities to randomly reconnect using a specified server list containing the servers of your # choice. Special care has been taken to ensure that only the VPN connections you want to have monitored are tended to. # This script will check the health of up to 5 VPN connections on a regular interval to see if monitored VPN conenctions # are connected, and sends a ping to a host of your choice through each active connection. If it finds that a connection # has been lost, it will execute a series of commands that will kill that single VPN client, and randomly picks one of # your specified servers to reconnect to for each VPN client. -# Last Modified: 2025-Aug-20 +# Last Modified: 2025-Aug-21 ########################################################################################## #Preferred standard router binaries path export PATH="/sbin:/bin:/usr/sbin:/usr/bin:$PATH" #Static Variables - please do not change -version="1.6.0b5" # Version tracker -beta=1 # Beta switch +version="1.6.0" # Version tracker +beta=0 # Beta switch screenshotmode=0 # Switch to present bogus info for screenshots apppath="/jffs/scripts/vpnmon-r3.sh" # Static path to the app logfile="/jffs/addons/vpnmon-r3.d/vpnmon-r3.log" # Static path to the log From 0234ea450da9bd2d0081bdf50cfddd209e2070f3 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Fri, 22 Aug 2025 00:11:17 +0000 Subject: [PATCH 46/48] Update version.txt --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 80f945a..dc1e644 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.6.0b4 +1.6.0 From f4c0d674965073488f1213225da9100132e1c855 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Fri, 22 Aug 2025 00:11:40 +0000 Subject: [PATCH 47/48] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index efddbb2..a6860a5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# VPNMON-R3 v1.6.0 Beta 5 +# VPNMON-R3 v1.6.0 Asus-Merlin OpenVPN/Wireguard Monitor R3 -Updated on 2025-Aug-20 +Updated on 2025-Aug-21 --- From 9ce7b0f07751e1add425d7fefa6d94d90d9b0da0 Mon Sep 17 00:00:00 2001 From: ViktorJp <97465574+ViktorJp@users.noreply.github.com> Date: Fri, 22 Aug 2025 00:12:14 +0000 Subject: [PATCH 48/48] Update changelog.txt --- changelog.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.txt b/changelog.txt index 721e86f..1d31f75 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,4 +1,4 @@ -v1.6.0b5 - (TBA) +v1.6.0 - (August 21, 2025) - MINOR: Very happy to announce that "Unbound-over-Wireguard" has actually become a reality! With huge help and guidance from @ZebMcKayhan, a methodology was developed that is easier, simpler and much more elegant than it's Unbound-over-VPN twin. In essence, VPNMON-R3 creates a new virtual br0 interface,