qbittorrent: harden OpenVPN bypass-route logic

Four improvements over the initial fix:
- ip route replace instead of add: silently idempotent on reconnects,
  no spurious "File exists" warnings
- State file tracking: bypass_routes records exactly which routes were
  added; postdown reads and removes only those, leaving any pre-existing
  host route to the VPN endpoint untouched
- IPv6 support: handle trusted_ip6 + route_ipv6_gateway with
  ip -6 route .../128 so IPv6-endpoint tunnels also get the bypass
- Qualified deletion: include the via <gw> qualifier when deleting,
  matching the exact route this script inserted

https://claude.ai/code/session_01EgUNs9abQ2s316Y2cRr1xc
This commit is contained in:
Claude committed 2026-05-12 12:46:56 +00:00
1 parent b0464c563c
commit 618da1f461
3 files changed
+48 -13

No files matched your search

+3
View File
@@ -1,3 +1,6 @@
## 5.2.0-10 (12-05-2026)
- Harden OpenVPN bypass-route logic: use ip route replace to avoid "File exists" warnings on reconnects; track added routes in a state file so postdown only removes routes this script created; add IPv6 endpoint support via trusted_ip6/route_ipv6_gateway; qualify route deletion with the exact gateway
## 5.2.0-9 (12-05-2026)
- Fix OpenVPN "Recursive routing detected, drop tun packet" warnings: add /32 bypass route for the VPN server endpoint via the real gateway in the postup script so OpenVPN's own reconnect traffic never goes through tun0
+1 -1
View File
@@ -143,4 +143,4 @@ schema:
slug: qbittorrent
udev: true
url: https://github.com/alexbelgium/hassio-addons
version: "5.2.0-9"
version: "5.2.0-10"
+44 -12
View File
@@ -537,16 +537,50 @@ _openvpn_down() {
_routing_del || true
}
_openpvn_postup() {
# Add a /32 bypass route for the VPN server via the real gateway so that
# OpenVPN's own reconnection traffic doesn't get routed back through tun0
# (which would trigger "Recursive routing detected" and dropped packets).
# trusted_ip and route_net_gateway are set by OpenVPN when calling --up scripts.
_openvpn_bypass_route_add() {
# OpenVPN sets trusted_ip/trusted_ip6 (server endpoint) and
# route_net_gateway/route_ipv6_gateway (real gateway) in --up scripts.
# We must add a bypass host-route for the server so that OpenVPN's own
# reconnect traffic is never forwarded through tun0 (recursive routing).
# Uses ip route replace so reconnects don't produce "File exists" warnings.
# Records added routes in a state file so postdown only removes what we added.
local state_file="${OPENVPN_STATE_DIR}/bypass_routes"
: > "${state_file}"
if [ -n "${trusted_ip:-}" ] && [ -n "${route_net_gateway:-}" ]; then
bashio::log.info "Adding bypass route for VPN server ${trusted_ip} via ${route_net_gateway}"
ip -4 route add "${trusted_ip}/32" via "${route_net_gateway}" 2>/dev/null \
|| bashio::log.warning "Could not add bypass route for VPN server ${trusted_ip} (non-fatal)."
bashio::log.info "Adding IPv4 bypass route for VPN server ${trusted_ip} via ${route_net_gateway}"
if ip -4 route replace "${trusted_ip}/32" via "${route_net_gateway}" 2>/dev/null; then
echo "4 ${trusted_ip}/32 ${route_net_gateway}" >> "${state_file}"
else
bashio::log.warning "Could not add IPv4 bypass route for VPN server ${trusted_ip} (non-fatal)."
fi
fi
if [ -n "${trusted_ip6:-}" ] && [ -n "${route_ipv6_gateway:-}" ]; then
bashio::log.info "Adding IPv6 bypass route for VPN server ${trusted_ip6} via ${route_ipv6_gateway}"
if ip -6 route replace "${trusted_ip6}/128" via "${route_ipv6_gateway}" 2>/dev/null; then
echo "6 ${trusted_ip6}/128 ${route_ipv6_gateway}" >> "${state_file}"
else
bashio::log.warning "Could not add IPv6 bypass route for VPN server ${trusted_ip6} (non-fatal)."
fi
fi
}
_openvpn_bypass_route_del() {
local state_file="${OPENVPN_STATE_DIR}/bypass_routes"
[ -f "${state_file}" ] || return 0
local af host gw
while read -r af host gw; do
bashio::log.info "Removing bypass route ${host} via ${gw}"
ip "-${af}" route del "${host}" via "${gw}" 2>/dev/null || true
done < "${state_file}"
rm -f "${state_file}"
}
_openpvn_postup() {
local OPENVPN_STATE_DIR="/var/run/openvpn"
_openvpn_bypass_route_add
# Add routing rules for VPN interface and DNS servers
_routing_add || return 1
# Add firewall rules for VPN interface (only when UPnP port mapping is enabled)
@@ -558,10 +592,8 @@ _openpvn_postup() {
}
_openpvn_postdown() {
# Remove the bypass route for the VPN server if it was added
if [ -n "${trusted_ip:-}" ]; then
ip -4 route del "${trusted_ip}/32" 2>/dev/null || true
fi
local OPENVPN_STATE_DIR="/var/run/openvpn"
_openvpn_bypass_route_del
# Update resolv.conf to remove VPN DNS servers
_resolvconf "reset" || true
# Remove routing rules for VPN interface and DNS servers