mirror of
https://github.com/caiwx86/small-packages.git
synced 2026-10-26 18:52:51 +08:00
125 lines
3.7 KiB
Bash
Executable File
125 lines
3.7 KiB
Bash
Executable File
#!/bin/sh
|
|
# Set CUBIC only for opted-in OpenVPN clients' outer TCP destinations.
|
|
|
|
umask 077
|
|
state=${OPENVPN_CLIENT_CUBIC_STATE:-/tmp/openvpn-client-cubic-routes}
|
|
|
|
sections() {
|
|
uci -q show openvpn 2>/dev/null |
|
|
sed -n 's/^openvpn\.\(client[A-Za-z0-9_-]*\)=openvpn$/\1/p'
|
|
}
|
|
|
|
client_enabled() {
|
|
[ "$(uci -q get "openvpn.$1.enabled")" = 1 ] &&
|
|
[ "$(uci -q get "openvpn.$1.tcp_cubic")" = 1 ]
|
|
}
|
|
|
|
has_clients() {
|
|
for section in $(sections); do
|
|
client_enabled "$section" && return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
delete_old_routes() {
|
|
[ -f "$state" ] || return 0
|
|
while IFS='|' read -r address via device; do
|
|
[ -n "$address" ] && [ -n "$device" ] || continue
|
|
if [ -n "$via" ]; then
|
|
ip -4 route del "$address/32" via "$via" dev "$device" 2>/dev/null
|
|
else
|
|
ip -4 route del "$address/32" dev "$device" 2>/dev/null
|
|
fi
|
|
done < "$state"
|
|
return 0
|
|
}
|
|
|
|
add_route() {
|
|
local address=$1 route via device
|
|
route=$(ip -4 route get "$address" 2>/dev/null) || return 1
|
|
set -- $route
|
|
while [ "$#" -gt 1 ]; do
|
|
case "$1" in
|
|
via) via=$2; shift 2 ;;
|
|
dev) device=$2; shift 2 ;;
|
|
*) shift ;;
|
|
esac
|
|
done
|
|
[ -n "$device" ] && [ "$device" != lo ] || return 1
|
|
if [ -n "$via" ]; then
|
|
ip -4 route replace "$address/32" via "$via" dev "$device" congctl cubic || return 1
|
|
else
|
|
ip -4 route replace "$address/32" dev "$device" congctl cubic || return 1
|
|
fi
|
|
printf '%s|%s|%s\n' "$address" "$via" "$device" >> "$new_state"
|
|
}
|
|
|
|
update_routes() {
|
|
local section conf host addresses address failed=0 desired new_state
|
|
desired=$(mktemp /tmp/openvpn-client-cubic-desired.XXXXXX) || return 1
|
|
new_state=$(mktemp /tmp/openvpn-client-cubic-state.XXXXXX) || {
|
|
rm -f "$desired"
|
|
return 1
|
|
}
|
|
|
|
for section in $(sections); do
|
|
client_enabled "$section" || continue
|
|
conf=$(uci -q get "openvpn.$section.config")
|
|
if [ ! -r "$conf" ]; then
|
|
logger -t openvpn-client-cubic "Cannot read config for $section"
|
|
failed=1
|
|
continue
|
|
fi
|
|
# An outer TCP congestion controller has no effect on UDP clients.
|
|
grep -Eiq '^[[:space:]]*proto[[:space:]]+tcp' "$conf" || continue
|
|
for host in $(awk '$1 == "remote" { gsub(/["\047]/, "", $2); print $2 }' "$conf"); do
|
|
case "$host" in
|
|
''|*[!0-9.]*) addresses=$(resolveip -4 -t 5 "$host" 2>/dev/null) ;;
|
|
*) addresses=$host ;;
|
|
esac
|
|
if [ -z "$addresses" ]; then
|
|
logger -t openvpn-client-cubic "Cannot resolve $host for $section"
|
|
failed=1
|
|
continue
|
|
fi
|
|
for address in $addresses; do
|
|
case "$address" in
|
|
*[!0-9.]*|'') continue ;;
|
|
esac
|
|
printf '%s\n' "$address" >> "$desired"
|
|
done
|
|
done
|
|
done
|
|
|
|
if [ "$failed" -ne 0 ]; then
|
|
rm -f "$desired" "$new_state"
|
|
return 1
|
|
fi
|
|
|
|
delete_old_routes
|
|
sort -u "$desired" | while IFS= read -r address; do
|
|
[ -n "$address" ] || continue
|
|
add_route "$address" || exit 1
|
|
done
|
|
failed=$?
|
|
rm -f "$desired"
|
|
if [ "$failed" -eq 0 ]; then
|
|
mv -f "$new_state" "$state"
|
|
else
|
|
rm -f "$new_state"
|
|
fi
|
|
return "$failed"
|
|
}
|
|
|
|
case "$1" in
|
|
enabled) has_clients ;;
|
|
update) update_routes ;;
|
|
clear) delete_old_routes; rm -f "$state" ;;
|
|
watch)
|
|
while sleep 300; do
|
|
update_routes || logger -t openvpn-client-cubic 'Could not refresh CUBIC routes'
|
|
done
|
|
;;
|
|
*) echo "Usage: $0 {enabled|update|clear|watch}" >&2; exit 2 ;;
|
|
esac
|