define Package/openconnect
SECTION:=net
CATEGORY:=Network
- DEPENDS:=+libxml2 +libopenssl +kmod-tun +vpnc-scripts
+ DEPENDS:=+libxml2 +libopenssl +kmod-tun +resolveip +!PACKAGE_netifd:vpnc-scripts
TITLE:=VPN client for Cisco's AnyConnect SSL VPN
URL:=http://www.infradead.org/openconnect/
SUBMENU:=VPN
endef
CONFIGURE_ARGS+=--disable-shared
+ifdef CONFIG_PACKAGE_netifd
+ CONFIGURE_ARGS += --with-vpnc-script=/lib/netifd/vpnc-script
-define Package/openconnect/install
+ define Package/openconnect/install
+ $(INSTALL_DIR) $(1)/lib/netifd/proto
+ $(INSTALL_BIN) ./files/openconnect.sh $(1)/lib/netifd/proto/
+ $(INSTALL_BIN) ./files/vpnc-script $(1)/lib/netifd/
+ $(INSTALL_DIR) $(1)/usr/sbin
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/openconnect $(1)/usr/sbin/
+ endef
+else
+ CONFIGURE_ARGS += --with-vpnc-script=/etc/vpnc/vpnc-script
+
+ define Package/openconnect/install
$(INSTALL_DIR) $(1)/lib/network
- $(INSTALL_BIN) ./files/openconnect.sh $(1)/lib/network/
+ $(INSTALL_BIN) ./files.old/openconnect.sh $(1)/lib/network/
$(INSTALL_DIR) $(1)/usr/sbin
- $(INSTALL_BIN) ./files/run-openconnect $(1)/usr/sbin/
+ $(INSTALL_BIN) ./files.old/run-openconnect $(1)/usr/sbin/
$(INSTALL_BIN) $(PKG_BUILD_DIR)/openconnect $(1)/usr/sbin/
-endef
+ endef
+endif
$(eval $(call BuildPackage,openconnect))
--- /dev/null
+find_gw() {
+ route -n | awk '$1 == "0.0.0.0" { print $2; exit }'
+}
+
+scan_openconnect() {
+ config_set "$1" device "vpn-$1"
+}
+
+stop_interface_openconnect() {
+ local config="$1"
+ local lock="/var/lock/openconnect-$config"
+
+ uci_set_state network "$config" up 0
+
+ lock "$lock"
+
+ SERVICE_PID_FILE="/var/run/openconnect-${config}.pid" \
+ SERVICE_SIG=HUP service_stop /bin/sh
+
+ remove_dns "$config"
+
+ lock -u "$lock"
+}
+
+setup_interface_openconnect() {
+ local config="$2"
+
+ /sbin/insmod tun 2>&- >&-
+
+ # creating the tunnel below will trigger a net subsystem event
+ # prevent it from touching or iface by disabling .auto here
+ uci_set_state network "$config" ifname "vpn-$config"
+ uci_set_state network "$config" auto 0
+ uci_set_state network "$config" up 1
+
+ SERVICE_PID_FILE="/var/run/openconnect-${config}.pid" \
+ SERVICE_WRITE_PID=1 SERVICE_DAEMONIZE=1 \
+ service_start /usr/sbin/run-openconnect $config
+}
--- /dev/null
+#!/bin/sh -x
+
+. /etc/functions.sh
+
+[ $# = 0 ] && { echo " $0 <group>"; exit; }
+
+include /lib/network
+scan_interfaces
+
+config="$1"
+export OPENWRT_INTERFACE="$config"
+
+config_get proto "$config" proto
+
+if [ "$proto" != "openconnect" ]; then
+ echo "Interface $config is $proto not openconnect" >&2
+ exit 1
+fi
+
+config_get device "$config" device
+
+local server
+config_get server "$config" server
+
+local port
+config_get port "$config" port
+if [ -n "$port" ]; then
+ args="$server:$port"
+else
+ args="$server"
+fi
+
+local cookie
+config_get cookie "$config" cookie
+[ -n "$cookie" ] && args="$args -C $cookie"
+
+local username
+config_get username "$config" username
+[ -n "$username" ] && args="$args -u $username"
+
+local password
+config_get password "$password" password
+
+/sbin/insmod tun
+
+local lock="/var/lock/openconnect-$config"
+
+# creating the tunnel below will trigger a net subsystem event
+# prevent it from touching or iface by disabling .auto here
+uci_set_state network "$config" ifname $link
+uci_set_state network "$config" auto 0
+
+local gw="$(find_gw)"
+[ -n "$gw" ] && {
+ local serv_addrs=""
+ for ip in $(resolveip -4 -t 3 "$server"); do
+ append serv_addrs "$ip"
+ route delete -host "$ip" 2>/dev/null
+ route add -host "$ip" gw "$gw"
+ done
+ uci_toggle_state network "$config" serv_addrs "$serv_addrs"
+}
+
+RECON=$(date +%s)
+
+trap "[ -r /var/run/openconnect-$config-oc.pid ] && kill -HUP \$(cat /var/run/openconnect-$config-oc.pid)" SIGHUP
+while [ "$(uci_get_state network ${config} up)" = "1" ]; do
+ NOW=$(date +%s)
+ if [ $RECON -gt $NOW ]; then
+ DELAY=$(expr $RECON - $NOW)
+ logger -t openconnect "Waiting for $DELAY seconds before reconnecting"
+ sleep $(expr $DELAY)
+ fi
+
+ # The lock prevents a race condition where /lib/network/openconnect.sh could
+ # send us SIGHUP after we spawn openconnect, but before we store its pid.
+ # Thus leaving it running after we should have killed it.
+ lock $lock
+ echo "$passwd" | /usr/sbin/openconnect $args -i "vpn-$config" \
+ --no-cert-check --non-inter --passwd-on-stdin --syslog --script /etc/vpnc/vpnc-script &
+ echo $! > /var/run/openconnect-$config-oc.pid
+ lock -u $lock
+ wait $!
+ rm /var/run/openconnect-$config-oc.pid
+ RECON=$(expr $NOW + 60)
+done
-find_gw() {
- route -n | awk '$1 == "0.0.0.0" { print $2; exit }'
+#!/bin/sh
+. /etc/functions.sh
+. ../netifd-proto.sh
+init_proto "$@"
+
+proto_openconnect_init_config() {
+ proto_config_add_string "server"
+ proto_config_add_int "port"
+ proto_config_add_string "username"
+ proto_config_add_string "cookie"
+ proto_config_add_string "password"
+ no_device=1
+ available=1
}
-scan_openconnect() {
- config_set "$1" device "vpn-$1"
-}
-
-stop_interface_openconnect() {
+proto_openconnect_setup() {
local config="$1"
- local lock="/var/lock/openconnect-$config"
-
- uci_set_state network "$config" up 0
-
- lock "$lock"
-
- SERVICE_PID_FILE="/var/run/openconnect-${config}.pid" \
- SERVICE_SIG=HUP service_stop /bin/sh
- remove_dns "$config"
-
- lock -u "$lock"
+ json_get_vars server port username cookie password
+
+ grep -q tun /proc/modules || insmod tun
+
+ serv_addr=
+ for ip in $(resolveip -t 5 "$server"); do
+ proto_add_host_dependency "$config" "$server"
+ serv_addr=1
+ done
+ [ -n "$serv_addr" ] || {
+ echo "Could not resolve server address"
+ sleep 5
+ proto_setup_failed "$config"
+ exit 1
+ }
+
+ [ -n "$port" ] && port=":$port"
+
+ cmdline="$server$port -i vpn-$config --no-cert-check --non-inter --syslog --script /lib/netifd/vpnc-script"
+
+ [ -n "$cookie" ] && append cmdline "-C $cookie"
+ [ -n "$username" ] && append cmdline "-u $username"
+ [ -n "$password" ] && {
+ umask 077
+ pwfile="/var/run/openconnect-$config.passwd"
+ echo "$password" > "$pwfile"
+ append cmdline "--passwd-file=$pwfile"
+ }
+
+ proto_export INTERFACE="$config"
+ proto_run_command "$config" /usr/sbin/openconnect $cmdline
}
-setup_interface_openconnect() {
- local config="$2"
-
- /sbin/insmod tun 2>&- >&-
-
- # creating the tunnel below will trigger a net subsystem event
- # prevent it from touching or iface by disabling .auto here
- uci_set_state network "$config" ifname "vpn-$config"
- uci_set_state network "$config" auto 0
- uci_set_state network "$config" up 1
-
- SERVICE_PID_FILE="/var/run/openconnect-${config}.pid" \
- SERVICE_WRITE_PID=1 SERVICE_DAEMONIZE=1 \
- service_start /usr/sbin/run-openconnect $config
+proto_openconnect_teardown() {
+ proto_kill_command "$config"
}
+
+add_protocol openconnect
+++ /dev/null
-#!/bin/sh -x
-
-. /etc/functions.sh
-
-[ $# = 0 ] && { echo " $0 <group>"; exit; }
-
-include /lib/network
-scan_interfaces
-
-config="$1"
-export OPENWRT_INTERFACE="$config"
-
-config_get proto "$config" proto
-
-if [ "$proto" != "openconnect" ]; then
- echo "Interface $config is $proto not openconnect" >&2
- exit 1
-fi
-
-config_get device "$config" device
-
-local server
-config_get server "$config" server
-
-local port
-config_get port "$config" port
-if [ -n "$port" ]; then
- args="$server:$port"
-else
- args="$server"
-fi
-
-local cookie
-config_get cookie "$config" cookie
-[ -n "$cookie" ] && args="$args -C $cookie"
-
-local username
-config_get username "$config" username
-[ -n "$username" ] && args="$args -u $username"
-
-local password
-config_get password "$password" password
-
-/sbin/insmod tun
-
-local lock="/var/lock/openconnect-$config"
-
-# creating the tunnel below will trigger a net subsystem event
-# prevent it from touching or iface by disabling .auto here
-uci_set_state network "$config" ifname $link
-uci_set_state network "$config" auto 0
-
-local gw="$(find_gw)"
-[ -n "$gw" ] && {
- local serv_addrs=""
- for ip in $(resolveip -4 -t 3 "$server"); do
- append serv_addrs "$ip"
- route delete -host "$ip" 2>/dev/null
- route add -host "$ip" gw "$gw"
- done
- uci_toggle_state network "$config" serv_addrs "$serv_addrs"
-}
-
-RECON=$(date +%s)
-
-trap "[ -r /var/run/openconnect-$config-oc.pid ] && kill -HUP \$(cat /var/run/openconnect-$config-oc.pid)" SIGHUP
-while [ "$(uci_get_state network ${config} up)" = "1" ]; do
- NOW=$(date +%s)
- if [ $RECON -gt $NOW ]; then
- DELAY=$(expr $RECON - $NOW)
- logger -t openconnect "Waiting for $DELAY seconds before reconnecting"
- sleep $(expr $DELAY)
- fi
-
- # The lock prevents a race condition where /lib/network/openconnect.sh could
- # send us SIGHUP after we spawn openconnect, but before we store its pid.
- # Thus leaving it running after we should have killed it.
- lock $lock
- echo "$passwd" | /usr/sbin/openconnect $args -i "vpn-$config" \
- --no-cert-check --non-inter --passwd-on-stdin --syslog --script /etc/vpnc/vpnc-script &
- echo $! > /var/run/openconnect-$config-oc.pid
- lock -u $lock
- wait $!
- rm /var/run/openconnect-$config-oc.pid
- RECON=$(expr $NOW + 60)
-done
--- /dev/null
+#!/bin/sh
+# List of parameters passed through environment
+#* reason -- why this script was called, one of: pre-init connect disconnect
+#* VPNGATEWAY -- vpn gateway address (always present)
+#* TUNDEV -- tunnel device (always present)
+#* INTERNAL_IP4_ADDRESS -- address (always present)
+#* INTERNAL_IP4_MTU -- mtu (often unset)
+#* INTERNAL_IP4_NETMASK -- netmask (often unset)
+#* INTERNAL_IP4_NETMASKLEN -- netmask length (often unset)
+#* INTERNAL_IP4_NETADDR -- address of network (only present if netmask is set)
+#* INTERNAL_IP4_DNS -- list of dns servers
+#* INTERNAL_IP4_NBNS -- list of wins servers
+#* INTERNAL_IP6_ADDRESS -- IPv6 address
+#* INTERNAL_IP6_NETMASK -- IPv6 netmask
+#* INTERNAL_IP6_DNS -- IPv6 list of dns servers
+#* CISCO_DEF_DOMAIN -- default domain name
+#* CISCO_BANNER -- banner from server
+#* CISCO_SPLIT_INC -- number of networks in split-network-list
+#* CISCO_SPLIT_INC_%d_ADDR -- network address
+#* CISCO_SPLIT_INC_%d_MASK -- subnet mask (for example: 255.255.255.0)
+#* CISCO_SPLIT_INC_%d_MASKLEN -- subnet masklen (for example: 24)
+#* CISCO_SPLIT_INC_%d_PROTOCOL -- protocol (often just 0)
+#* CISCO_SPLIT_INC_%d_SPORT -- source port (often just 0)
+#* CISCO_SPLIT_INC_%d_DPORT -- destination port (often just 0)
+#* CISCO_IPV6_SPLIT_INC -- number of networks in IPv6 split-network-list
+#* CISCO_IPV6_SPLIT_INC_%d_ADDR -- IPv6 network address
+#* CISCO_IPV6_SPLIT_INC_$%d_MASKLEN -- IPv6 subnet masklen
+
+# FIXMEs:
+
+# Section A: route handling
+
+# 1) The 3 values CISCO_SPLIT_INC_%d_PROTOCOL/SPORT/DPORT are currently being ignored
+# In order to use them, we'll probably need os specific solutions
+# * Linux: iptables -t mangle -I PREROUTING <conditions> -j ROUTE --oif $TUNDEV
+# This would be an *alternative* to changing the routes (and thus 2) and 3)
+# shouldn't be relevant at all)
+# 2) There are two different functions to set routes: generic routes and the
+# default route. Why isn't the defaultroute handled via the generic route case?
+# 3) In the split tunnel case, all routes but the default route might get replaced
+# without getting restored later. We should explicitely check and save them just
+# like the defaultroute
+# 4) Replies to a dhcp-server should never be sent into the tunnel
+
+# Section B: Split DNS handling
+
+# 1) Maybe dnsmasq can do something like that
+# 2) Parse dns packets going out via tunnel and redirect them to original dns-server
+
+do_connect() {
+ if [ -n "$CISCO_BANNER" ]; then
+ echo "Connect Banner:"
+ echo "$CISCO_BANNER" | while read LINE ; do echo "|" "$LINE" ; done
+ echo
+ fi
+
+ proto_init_update "$TUNDEV" 1
+
+ if [ -n "$INTERNAL_IP4_MTU" ]; then
+ MTU=$INTERNAL_IP4_MTU
+ fi
+
+ if [ -z "$MTU" ]; then
+ MTU=1412
+ fi
+
+ proto_add_ipv4_address "$INTERNAL_IP4_ADDRESS" 32 "" "$INTERNAL_IP4_ADDRESS"
+
+ if [ -n "$INTERNAL_IP4_NETMASKLEN" ]; then
+ proto_add_ipv4_route "$INTERNAL_IP4_NETADDR" "$INTERNAL_IP4_NETMASKLEN"
+ fi
+
+ # If the netmask is provided, it contains the address _and_ netmask
+ if [ -n "$INTERNAL_IP6_ADDRESS" ] && [ -z "$INTERNAL_IP6_NETMASK" ]; then
+ INTERNAL_IP6_NETMASK="$INTERNAL_IP6_ADDRESS/128"
+ fi
+
+ if [ -n "$INTERNAL_IP6_NETMASK" ]; then
+ addr="${INTERNAL_IP6_NETMASK%%/*}"
+ mask="${INTERNAL_IP6_NETMASK##*/}"
+ [[ "$addr" != "$mask" ]] && proto_add_ipv6_address "$addr" "$mask"
+ fi
+
+ [ -n "$INTERNAL_IP4_DNS" ] && proto_add_dns_server "$INTERNAL_IP4_DNS"
+ [ -n "$CISCO_DEF_DOMAIN" ] && proto_add_dns_search "$CISCO_DEF_DOMAIN"
+
+ if [ -n "$CISCO_SPLIT_INC" ]; then
+ i=0
+ while [ $i -lt $CISCO_SPLIT_INC ] ; do
+ eval NETWORK="\${CISCO_SPLIT_INC_${i}_ADDR}"
+ eval NETMASK="\${CISCO_SPLIT_INC_${i}_MASK}"
+ eval NETMASKLEN="\${CISCO_SPLIT_INC_${i}_MASKLEN}"
+ if [ $NETWORK != "0.0.0.0" ]; then
+ proto_add_ipv4_route "$NETWORK" "$NETMASKLEN"
+ else
+ proto_add_ipv4_route "0.0.0.0" 0
+ fi
+ i=$(($i + 1))
+ done
+ elif [ -n "$INTERNAL_IP4_ADDRESS" ]; then
+ proto_add_ipv4_route "0.0.0.0" 0
+ fi
+ if [ -n "$CISCO_IPV6_SPLIT_INC" ]; then
+ i=0
+ while [ $i -lt $CISCO_IPV6_SPLIT_INC ] ; do
+ eval NETWORK="\${CISCO_IPV6_SPLIT_INC_${i}_ADDR}"
+ eval NETMASKLEN="\${CISCO_IPV6_SPLIT_INC_${i}_MASKLEN}"
+ if [ $NETMASKLEN -lt 128 ]; then
+ proto_add_ipv6_route "$NETWORK" "$NETMASKLEN"
+ else
+ proto_add_ipv6_route "::0" 0
+ fi
+ i=$(($i + 1))
+ done
+ elif [ -n "$INTERNAL_IP6_NETMASK" -o -n "$INTERNAL_IP6_ADDRESS" ]; then
+ proto_add_ipv6_route "::0" 0
+ fi
+ proto_send_update "$INTERFACE"
+}
+
+do_disconnect() {
+ proto_init_update "$TUNDEV" 0
+ proto_send_update "$INTERFACE"
+}
+
+#### Main
+
+if [ -z "$reason" ]; then
+ echo "this script must be called from vpnc" 1>&2
+ exit 1
+fi
+if [ -z "$INTERFACE" ]; then
+ echo "this script must be called for an active interface"
+ exit 1
+fi
+
+. /lib/netifd/netifd-proto.sh
+
+case "$reason" in
+ pre-init)
+ ;;
+ connect)
+ do_connect
+ ;;
+ disconnect)
+ do_disconnect
+ ;;
+ reconnect)
+ ;;
+ *)
+ echo "unknown reason '$reason'. Maybe vpnc-script is out of date" 1>&2
+ exit 1
+ ;;
+esac
+
+exit 0
--- /dev/null
+--- a/main.c
++++ b/main.c
+@@ -77,6 +77,7 @@ enum {
+ OPT_CAFILE,
+ OPT_COOKIEONLY,
+ OPT_COOKIE_ON_STDIN,
++ OPT_COOKIE_FILE,
+ OPT_CSD_USER,
+ OPT_CSD_WRAPPER,
+ OPT_DISABLE_IPV6,
+@@ -91,6 +92,7 @@ enum {
+ OPT_NO_PROXY,
+ OPT_PIDFILE,
+ OPT_PASSWORD_ON_STDIN,
++ OPT_PASSWORD_FILE,
+ OPT_PRINTCOOKIE,
+ OPT_RECONNECT_TIMEOUT,
+ OPT_SERVERCERT,
+@@ -139,7 +141,9 @@ static struct option long_options[] = {
+ OPTION("queue-len", 1, 'Q'),
+ OPTION("xmlconfig", 1, 'x'),
+ OPTION("cookie-on-stdin", 0, OPT_COOKIE_ON_STDIN),
++ OPTION("cookie-file", 1, OPT_COOKIE_FILE),
+ OPTION("passwd-on-stdin", 0, OPT_PASSWORD_ON_STDIN),
++ OPTION("passwd-file", 1, OPT_PASSWORD_FILE),
+ OPTION("no-passwd", 0, OPT_NO_PASSWD),
+ OPTION("reconnect-timeout", 1, OPT_RECONNECT_TIMEOUT),
+ OPTION("dtls-ciphers", 1, OPT_DTLS_CIPHERS),
+@@ -177,6 +181,7 @@ static void usage(void)
+ printf(" -K, --key-type=TYPE %s\n", _("Private key type (PKCS#12 / TPM / PEM)"));
+ printf(" -C, --cookie=COOKIE %s\n", _("Use WebVPN cookie COOKIE"));
+ printf(" --cookie-on-stdin %s\n", _("Read cookie from standard input"));
++ printf(" --cookie-file=FILE %s\n", _("Read cookie from a file"));
+ printf(" -d, --deflate %s\n", _("Enable compression (default)"));
+ printf(" -D, --no-deflate %s\n", _("Disable compression"));
+ printf(" --force-dpd=INTERVAL %s\n", _("Set minimum Dead Peer Detection interval"));
+@@ -217,6 +222,7 @@ static void usage(void)
+ printf(" --no-cert-check %s\n", _("Do not require server SSL cert to be valid"));
+ printf(" --non-inter %s\n", _("Do not expect user input; exit if it is required"));
+ printf(" --passwd-on-stdin %s\n", _("Read password from standard input"));
++ printf(" --passwd-file=FILE %s\n", _("Read password from a file"));
+ printf(" --reconnect-timeout %s\n", _("Connection retry timeout in seconds"));
+ printf(" --servercert=FINGERPRINT %s\n", _("Server's certificate SHA1 fingerprint"));
+ printf(" --useragent=STRING %s\n", _("HTTP header User-Agent: field"));
+@@ -226,15 +232,28 @@ static void usage(void)
+ exit(1);
+ }
+
+-static void read_stdin(char **string)
++static void read_file(const char *file, char **string)
+ {
+ char *c = malloc(100);
++ FILE *f;
++
++ if (file) {
++ f = fopen(file, "r");
++ if (!f) {
++ fprintf(stderr, _("Failed to open password file\n"));
++ exit(1);
++ }
++ } else {
++ file = "stdin";
++ f = stdin;
++ }
++
+ if (!c) {
+- fprintf(stderr, _("Allocation failure for string from stdin\n"));
++ fprintf(stderr, _("Allocation failure for string from %s\n"), file);
+ exit(1);
+ }
+- if (!fgets(c, 100, stdin)) {
+- perror(_("fgets (stdin)"));
++ if (!fgets(c, 100, f)) {
++ perror(_("fgets"));
+ exit(1);
+ }
+
+@@ -332,14 +351,20 @@ int main(int argc, char **argv)
+ cookieonly = 2;
+ break;
+ case OPT_COOKIE_ON_STDIN:
+- read_stdin(&vpninfo->cookie);
++ optarg = NULL;
++ /* fall through */
++ case OPT_COOKIE_FILE:
++ read_file(optarg, &vpninfo->cookie);
+ /* If the cookie is empty, ignore it */
+ if (! *vpninfo->cookie) {
+ vpninfo->cookie = NULL;
+ }
+ break;
+ case OPT_PASSWORD_ON_STDIN:
+- read_stdin(&vpninfo->password);
++ optarg = NULL;
++ /* fall through */
++ case OPT_PASSWORD_FILE:
++ read_file(optarg, &vpninfo->password);
+ break;
+ case OPT_NO_PASSWD:
+ vpninfo->nopasswd = 1;