|
|
|
@ -69,8 +69,13 @@
|
|
|
|
|
# 1) Maybe dnsmasq can do something like that
|
|
|
|
|
# 2) Parse dns packets going out via tunnel and redirect them to original dns-server
|
|
|
|
|
|
|
|
|
|
#env | sort
|
|
|
|
|
#set -x
|
|
|
|
|
# ======== For test logging (CI/CD will uncomment automatically) =========
|
|
|
|
|
|
|
|
|
|
#TRACE# echo "------------------"
|
|
|
|
|
#TRACE# echo "vpnc-script environment:"
|
|
|
|
|
#TRACE# env | egrep '^(CISCO_|INTERNAL_IP|VPNGATEWAY|TUNDEV|reason)' | sort
|
|
|
|
|
#TRACE# echo "------------------"
|
|
|
|
|
#TRACE# set -x
|
|
|
|
|
|
|
|
|
|
# =========== script (variable) setup ====================================
|
|
|
|
|
|
|
|
|
@ -79,7 +84,8 @@ PATH=/sbin:/usr/sbin:$PATH
|
|
|
|
|
OS="`uname -s`"
|
|
|
|
|
|
|
|
|
|
HOOKS_DIR=/etc/vpnc
|
|
|
|
|
DEFAULT_ROUTE_FILE=/var/run/vpnc/defaultroute
|
|
|
|
|
DEFAULT_ROUTE_FILE=/var/run/vpnc/defaultroute.${PPID}
|
|
|
|
|
DEFAULT_ROUTE_FILE_IPV6=/var/run/vpnc/defaultroute_ipv6.${PPID}
|
|
|
|
|
RESOLV_CONF_BACKUP=/var/run/vpnc/resolv.conf-backup
|
|
|
|
|
SCRIPTNAME=`basename $0`
|
|
|
|
|
|
|
|
|
@ -101,6 +107,12 @@ if [ "$OS" = "Linux" ]; then
|
|
|
|
|
route_syntax_gw="gw"
|
|
|
|
|
route_syntax_del="del"
|
|
|
|
|
route_syntax_netmask="netmask"
|
|
|
|
|
route_syntax_inet6="-6"
|
|
|
|
|
route_syntax_inet6_host="-6"
|
|
|
|
|
route_syntax_inet6_net="-6"
|
|
|
|
|
ifconfig_syntax_add_inet6="add"
|
|
|
|
|
ifconfig_syntax_del="del"
|
|
|
|
|
netstat_syntax_ipv6="-6"
|
|
|
|
|
else
|
|
|
|
|
# iproute2 is Linux only; if `which ip` returns something on another OS, it's likely an unrelated tool
|
|
|
|
|
# (see https://github.com/dlenski/openconnect/issues/132#issuecomment-470475009)
|
|
|
|
@ -109,6 +121,12 @@ else
|
|
|
|
|
route_syntax_gw=""
|
|
|
|
|
route_syntax_del="delete"
|
|
|
|
|
route_syntax_netmask="-netmask"
|
|
|
|
|
route_syntax_inet6="-inet6"
|
|
|
|
|
route_syntax_inet6_host="-inet6 -host"
|
|
|
|
|
route_syntax_inet6_net="-inet6 -net"
|
|
|
|
|
ifconfig_syntax_del="delete"
|
|
|
|
|
ifconfig_syntax_add_inet6="inet6"
|
|
|
|
|
netstat_syntax_ipv6="-f inet6"
|
|
|
|
|
fi
|
|
|
|
|
if [ "$OS" = "SunOS" ]; then
|
|
|
|
|
route_syntax_interface="-interface"
|
|
|
|
@ -118,7 +136,7 @@ else
|
|
|
|
|
ifconfig_syntax_ptpv6=""
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
grep ^hosts /etc/nsswitch.conf 2>/dev/null|grep resolve >/dev/null 2>&1
|
|
|
|
|
grep '^hosts' /etc/nsswitch.conf 2>/dev/null|grep resolve >/dev/null 2>&1 && command systemd-resolve --status >/dev/null 2>&1
|
|
|
|
|
if [ $? = 0 ];then
|
|
|
|
|
RESOLVEDENABLED=1
|
|
|
|
|
else
|
|
|
|
@ -138,8 +156,9 @@ elif [ -x /usr/bin/busctl ] && [ ${RESOLVEDENABLED} = 1 ]; then
|
|
|
|
|
# For systemd-resolved (version 229 and above)
|
|
|
|
|
MODIFYRESOLVCONF=modify_resolved_manager_old
|
|
|
|
|
RESTORERESOLVCONF=restore_resolved_manager_old
|
|
|
|
|
elif [ -x /sbin/resolvconf ]; then
|
|
|
|
|
elif [ -x /sbin/resolvconf ] && [ `basename $(readlink /sbin/resolvconf)` != resolvectl ]; then
|
|
|
|
|
# Optional tool on Debian, Ubuntu, Gentoo and FreeBSD
|
|
|
|
|
# (ignored if symlink to resolvctl, created by some versions of systemd-resolved)
|
|
|
|
|
MODIFYRESOLVCONF=modify_resolvconf_manager
|
|
|
|
|
RESTORERESOLVCONF=restore_resolvconf_manager
|
|
|
|
|
elif [ -x /sbin/netconfig ] && [ ! -f /etc/slackware-version ]; then
|
|
|
|
@ -216,7 +235,7 @@ do_ifconfig() {
|
|
|
|
|
# OpenVPN does the same (gives dest_address for Legacy IP
|
|
|
|
|
# but not for IPv6).
|
|
|
|
|
# Only Solaris needs it; hence $ifconfig_syntax_ptpv6
|
|
|
|
|
ifconfig "$TUNDEV" inet6 $INTERNAL_IP6_NETMASK $ifconfig_syntax_ptpv6 mtu $MTU up
|
|
|
|
|
ifconfig "$TUNDEV" $ifconfig_syntax_add_inet6 $INTERNAL_IP6_NETMASK $ifconfig_syntax_ptpv6 mtu $MTU up
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
@ -237,7 +256,7 @@ destroy_tun_device() {
|
|
|
|
|
if [ -n "$IPROUTE" ]; then
|
|
|
|
|
fix_ip_get_output () {
|
|
|
|
|
sed -e 's/ /\n/g' | \
|
|
|
|
|
sed -ne "1 s|\$|${1}|p;/via/{N;p};/dev/{N;p};/src/{N;p};/mtu/{N;p}"
|
|
|
|
|
sed -ne "1 s|\$|${1}|p;/via/{N;p};/dev/{N;p};/src/{N;p};/mtu/{N;p};/metric/{N;p}"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_vpngateway_route() {
|
|
|
|
@ -245,6 +264,24 @@ if [ -n "$IPROUTE" ]; then
|
|
|
|
|
$IPROUTE route flush cache 2>/dev/null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_vpngateway_route_attempt_reconnect() {
|
|
|
|
|
# We'll attempt to add a host route to the gateway through every route that matches
|
|
|
|
|
# its address (excluding those through TUNDEV because the goal is to avoid loopback).
|
|
|
|
|
|
|
|
|
|
echo "$VPNGATEWAY" | grep -q : && FAMILY=-6 ROOT=::/0 || FAMILY=-4 ROOT=0/0
|
|
|
|
|
# put metric in front, sort by metric, then chop off first two fields (metric and destination)
|
|
|
|
|
$IPROUTE $FAMILY route show to "$VPNGATEWAY" root "$ROOT" |
|
|
|
|
|
awk '/dev '"$TUNDEV"'/ { next; } { printf "%s %s\n", (match($0, /metric ([^ ]+)/) ? substr($0, RSTART+7, RLENGTH-7) : 4294967295), $0; }' |
|
|
|
|
|
sort -n | cut -d' ' -f3- |
|
|
|
|
|
while read LINE ; do
|
|
|
|
|
# We do not want to use 'replace', since a route to the gateway that already
|
|
|
|
|
# exists is mostly likely the correct one (e.g. the case of a reconnect attempt
|
|
|
|
|
# after dead-peer detection, but no change in the underlying network devices).
|
|
|
|
|
$IPROUTE $FAMILY route add `echo "$VPNGATEWAY $LINE" | fix_ip_get_output` 2>/dev/null
|
|
|
|
|
done
|
|
|
|
|
$IPROUTE $FAMILY route flush cache 2>/dev/null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
del_vpngateway_route() {
|
|
|
|
|
$IPROUTE route $route_syntax_del "$VPNGATEWAY"
|
|
|
|
|
$IPROUTE route flush cache 2>/dev/null
|
|
|
|
@ -362,15 +399,29 @@ else # use route command
|
|
|
|
|
# apperently not...
|
|
|
|
|
# Get rid of lines containing IPv6 addresses (':')
|
|
|
|
|
# Get rid of lines for link-local routes (https://superuser.com/a/1067742)
|
|
|
|
|
netstat -r -n | awk '/:/ { next; } /link\#/ { next; } /^(default|0\.0\.0\.0)/ { print $2; }'
|
|
|
|
|
# Get rid of lines containing $TUNDEV (we don't want loopback)
|
|
|
|
|
netstat -r -n | awk '/:/ { next; } /link\#/ { next; } /\s'"$TUNDEV"'(\s|$)/ { next; } /^(default|0\.0\.0\.0)/ { print $2; exit; }'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_vpngateway_route() {
|
|
|
|
|
route add -host "$VPNGATEWAY" $route_syntax_gw "`get_default_gw`"
|
|
|
|
|
# Unlike with iproute2, there is no way to determine which current
|
|
|
|
|
# route(s) match the VPN gateway, so we simply find a default
|
|
|
|
|
# route and use its gateway.
|
|
|
|
|
case "$VPNGATEWAY" in
|
|
|
|
|
*:*) route add $route_syntax_inet6_host "$VPNGATEWAY" $route_syntax_gw "`get_ipv6_default_gw`";;
|
|
|
|
|
*) route add -host "$VPNGATEWAY" $route_syntax_gw "`get_default_gw`";;
|
|
|
|
|
esac
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_vpngateway_route_attempt_reconnect() {
|
|
|
|
|
set_vpngateway_route
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
del_vpngateway_route() {
|
|
|
|
|
route $route_syntax_del -host "$VPNGATEWAY" $route_syntax_gw "`get_default_gw`"
|
|
|
|
|
case "$VPNGATEWAY" in
|
|
|
|
|
*:*) route $route_syntax_del $route_syntax_inet6_host "$VPNGATEWAY" $route_syntax_gw "`get_ipv6_default_gw`";;
|
|
|
|
|
*) route $route_syntax_del -host "$VPNGATEWAY" $route_syntax_gw "`get_default_gw`";;
|
|
|
|
|
esac
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_default_route() {
|
|
|
|
@ -434,20 +485,34 @@ else # use route command
|
|
|
|
|
route $route_syntax_del -net "$NETWORK" $route_syntax_netmask "$NETMASK" $route_syntax_gw "$NETGW"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get_ipv6_default_gw() {
|
|
|
|
|
# isn't -n supposed to give --numeric output?
|
|
|
|
|
# apperently not...
|
|
|
|
|
# FIXME: is there a better way to exclude loopback routes than filtering interface /^lo/?
|
|
|
|
|
netstat -r -n $netstat_syntax_ipv6 | awk '/^(default|::\/0)/ { if ($NF!~/^lo/) { print ($2~/^fe[89ab]/ ? $2"%"$NF : $2); } }'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_ipv6_default_route() {
|
|
|
|
|
route add -inet6 default "$INTERNAL_IP6_ADDRESS" $route_syntax_interface
|
|
|
|
|
DEFAULTGW="`get_ipv6_default_gw`"
|
|
|
|
|
echo "$DEFAULTGW" > "$DEFAULT_ROUTE_FILE_IPV6"
|
|
|
|
|
route $route_syntax_del $route_syntax_inet6 default $route_syntax_gw "$DEFAULTGW"
|
|
|
|
|
route add $route_syntax_inet6 default $route_syntax_gw "$INTERNAL_IP6_ADDRESS" $route_syntax_interface
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
set_ipv6_network_route() {
|
|
|
|
|
NETWORK="$1"
|
|
|
|
|
NETMASK="$2"
|
|
|
|
|
DEVICE="$3"
|
|
|
|
|
if [ -n "$4" ]; then
|
|
|
|
|
NETGW="$4"
|
|
|
|
|
elif [ "$OS" = "Linux" ]; then
|
|
|
|
|
route add $route_syntax_inet6_net "$NETWORK/$NETMASK" dev "$DEVICE"
|
|
|
|
|
return
|
|
|
|
|
else
|
|
|
|
|
NETGW="$INTERNAL_IP6_ADDRESS"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
route add -inet6 -net "$NETWORK/$NETMASK" "$NETGW" $route_syntax_interface
|
|
|
|
|
route add $route_syntax_inet6_net "$NETWORK/$NETMASK" $route_syntax_gw "$NETGW" $route_syntax_interface
|
|
|
|
|
:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -457,31 +522,39 @@ else # use route command
|
|
|
|
|
# Add explicit route to keep traffic for this target separate
|
|
|
|
|
# from tunnel. FIXME: We use default gateway - this is our best
|
|
|
|
|
# guess in absence of "ip" command to query effective route.
|
|
|
|
|
route add -inet6 -net "$NETWORK/$NETMASK" "`get_default_gw`" $route_syntax_interface
|
|
|
|
|
route add $route_syntax_inet6_net "$NETWORK/$NETMASK" "`get_ipv6_default_gw`" $route_syntax_interface
|
|
|
|
|
:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reset_ipv6_default_route() {
|
|
|
|
|
route $route_syntax_del -inet6 default "$INTERNAL_IP6_ADDRESS"
|
|
|
|
|
if [ -s "$DEFAULT_ROUTE_FILE_IPV6" ]; then
|
|
|
|
|
route $route_syntax_del $route_syntax_inet6 default $route_syntax_gw "`get_ipv6_default_gw`" $route_syntax_interface
|
|
|
|
|
route add $route_syntax_inet6 default $route_syntax_gw `cat "$DEFAULT_ROUTE_FILE_IPV6"`
|
|
|
|
|
rm -f -- "$DEFAULT_ROUTE_FILE_IPV6"
|
|
|
|
|
fi
|
|
|
|
|
:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
del_ipv6_network_route() {
|
|
|
|
|
NETWORK="$1"
|
|
|
|
|
NETMASK="$2"
|
|
|
|
|
DEVICE="$3"
|
|
|
|
|
if [ -n "$4" ]; then
|
|
|
|
|
NETGW="$4"
|
|
|
|
|
elif [ "$OS" = "Linux" ]; then
|
|
|
|
|
route $route_syntax_del $route_syntax_inet6 "$NETWORK/$NETMASK" dev "$DEVICE"
|
|
|
|
|
return
|
|
|
|
|
else
|
|
|
|
|
NETGW="$INTERNAL_IP6_ADDRESS"
|
|
|
|
|
fi
|
|
|
|
|
route $route_syntax_del -inet6 "$NETWORK/$NETMASK" "$NETGW"
|
|
|
|
|
route $route_syntax_del $route_syntax_inet6 "$NETWORK/$NETMASK" $route_syntax_gw "$NETGW"
|
|
|
|
|
:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
del_ipv6_exclude_route() {
|
|
|
|
|
NETWORK="$1"
|
|
|
|
|
NETMASK="$2"
|
|
|
|
|
route $route_syntax_del -inet6 "$NETWORK/$NETMASK"
|
|
|
|
|
route $route_syntax_del $route_syntax_inet6 "$NETWORK/$NETMASK"
|
|
|
|
|
:
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -879,7 +952,10 @@ do_connect() {
|
|
|
|
|
echo
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
set_vpngateway_route
|
|
|
|
|
case "$VPNGATEWAY" in
|
|
|
|
|
127.*|::1) ;; # localhost (probably proxy)
|
|
|
|
|
*) set_vpngateway_route ;;
|
|
|
|
|
esac
|
|
|
|
|
do_ifconfig
|
|
|
|
|
if [ -n "$CISCO_SPLIT_EXC" ]; then
|
|
|
|
|
i=0
|
|
|
|
@ -887,7 +963,10 @@ do_connect() {
|
|
|
|
|
eval NETWORK="\${CISCO_SPLIT_EXC_${i}_ADDR}"
|
|
|
|
|
eval NETMASK="\${CISCO_SPLIT_EXC_${i}_MASK}"
|
|
|
|
|
eval NETMASKLEN="\${CISCO_SPLIT_EXC_${i}_MASKLEN}"
|
|
|
|
|
set_exclude_route "$NETWORK" "$NETMASK" "$NETMASKLEN"
|
|
|
|
|
case "$NETWORK" in
|
|
|
|
|
0.*|127.*|169.254.*) echo "ignoring non-forwardable exclude route $NETWORK/$NETMASKLEN" >&2 ;;
|
|
|
|
|
*) set_exclude_route "$NETWORK" "$NETMASK" "$NETMASKLEN" ;;
|
|
|
|
|
esac
|
|
|
|
|
i=`expr $i + 1`
|
|
|
|
|
done
|
|
|
|
|
fi
|
|
|
|
@ -1034,13 +1113,17 @@ do_disconnect() {
|
|
|
|
|
INTERNAL_IP6_NETMASK="$INTERNAL_IP6_ADDRESS/128"
|
|
|
|
|
fi
|
|
|
|
|
if [ -n "$INTERNAL_IP6_NETMASK" ]; then
|
|
|
|
|
ifconfig "$TUNDEV" inet6 del $INTERNAL_IP6_NETMASK
|
|
|
|
|
ifconfig "$TUNDEV" inet6 $ifconfig_syntax_del $INTERNAL_IP6_NETMASK
|
|
|
|
|
fi
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
destroy_tun_device
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
do_attempt_reconnect() {
|
|
|
|
|
set_vpngateway_route_attempt_reconnect
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#### Main
|
|
|
|
|
|
|
|
|
|
if [ -z "$reason" ]; then
|
|
|
|
@ -1069,6 +1152,8 @@ case "$reason" in
|
|
|
|
|
# be left with a route to the VPN server through the VPN
|
|
|
|
|
# itself, which would need to be fixed.
|
|
|
|
|
run_hooks attempt-reconnect
|
|
|
|
|
do_attempt_reconnect
|
|
|
|
|
run_hooks post-attempt-reconnect
|
|
|
|
|
;;
|
|
|
|
|
reconnect)
|
|
|
|
|
# After successfully re-establishing the session.
|
|
|
|
|