Created
January 2, 2026 00:35
-
-
Save oniGino/5dd9386eb9cfc7bc625afac28521b18b to your computer and use it in GitHub Desktop.
ProtonVPN UPNP Port Forwarding Script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/bash | |
| # CONFIGURATION | |
| GATEWAY="10.2.0.1" | |
| LIFETIME=60 | |
| SLEEP_TIME=45 | |
| NFT_TABLE="inet main" | |
| NFT_CHAIN="INPUT" | |
| NFT_COMMENT="UPnP-Loop" | |
| added_tcp_rule=0 | |
| added_udp_rule=0 | |
| tcp_port="" | |
| udp_port="" | |
| print_log() { | |
| local msg="$1" | |
| local now | |
| now=$(date '+%Y-%m-%d %H:%M:%S') | |
| echo "[$now] $msg" | |
| } | |
| add_nft_rule() { | |
| local proto="$1" | |
| local port="$2" | |
| print_log "Adding nft rule for $proto port $port" | |
| nft add rule "$NFT_TABLE" "$NFT_CHAIN" "$proto" dport "$port" accept comment "$NFT_COMMENT" | |
| transmission-remote --port $port | |
| } | |
| cleanup_upnp_rules() { | |
| # Delete all rules having our comment, regardless of proto or port | |
| print_log "Deleting all nft $NFT_TABLE $NFT_CHAIN rules with comment \"$NFT_COMMENT\"" | |
| handles=$(nft --json list chain "$NFT_TABLE" "$NFT_CHAIN" 2>/dev/null | \ | |
| jq -r --arg comment "$NFT_COMMENT" \ | |
| '.nftables[].rule | select(.comment == $comment) | .handle') | |
| for h in $handles; do | |
| print_log "Deleting rule with handle $h" | |
| nft delete rule "$NFT_TABLE" "$NFT_CHAIN" handle "$h" | |
| done | |
| print_log "Cleanup complete. Exiting." | |
| } | |
| trap cleanup_upnp_rules EXIT | |
| forward_port() { | |
| local proto="$1" | |
| forward_port_output=$(natpmpc -a 1 0 $proto $LIFETIME -g $GATEWAY 2>&1) | |
| forward_port_status=$? | |
| forward_port_port=$(echo "$forward_port_output" | awk "/Mapped public port/ && /${proto^^}/ {print \$4}") | |
| } | |
| while true; do | |
| print_log "Requesting port mapping..." | |
| # Forward UDP | |
| forward_port "udp" | |
| udp_status=$forward_port_status | |
| udp_port_new=$forward_port_port | |
| udp_output=$forward_port_output | |
| if [ "$udp_status" -ne 0 ]; then | |
| print_log "ERROR: natpmpc UDP mapping failed (status: $udp_status)" | |
| print_log "Output: $udp_output" | |
| break | |
| fi | |
| if [ "$added_udp_rule" -eq 0 ] && [ -n "$udp_port_new" ]; then | |
| udp_port="$udp_port_new" | |
| add_nft_rule "udp" "$udp_port" | |
| added_udp_rule=1 | |
| fi | |
| # Forward TCP | |
| forward_port "tcp" | |
| tcp_status=$forward_port_status | |
| tcp_port_new=$forward_port_port | |
| tcp_output=$forward_port_output | |
| if [ "$tcp_status" -ne 0 ]; then | |
| print_log "ERROR: natpmpc TCP mapping failed (status: $tcp_status)" | |
| print_log "Output: $tcp_output" | |
| break | |
| fi | |
| if [ "$added_tcp_rule" -eq 0 ] && [ -n "$tcp_port_new" ]; then | |
| tcp_port="$tcp_port_new" | |
| add_nft_rule "tcp" "$tcp_port" | |
| added_tcp_rule=1 | |
| fi | |
| print_log "UDP mapped to: $udp_port" | |
| print_log "TCP mapped to: $tcp_port" | |
| print_log "Sleeping for $SLEEP_TIME seconds..." | |
| sleep $SLEEP_TIME | |
| done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment