diff --git a/net/strongswan/Makefile b/net/strongswan/Makefile index 06ec598ba..7b8e215b7 100644 --- a/net/strongswan/Makefile +++ b/net/strongswan/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=strongswan PKG_VERSION:=5.9.1 -PKG_RELEASE:=7 +PKG_RELEASE:=8 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2 PKG_SOURCE_URL:=https://download.strongswan.org/ https://download2.strongswan.org/ @@ -537,11 +537,14 @@ define Package/strongswan-swanctl/conffiles endef define Package/strongswan-swanctl/install + $(INSTALL_DIR) $(1)/etc/init.d $(INSTALL_DIR) $(1)/etc/swanctl/{bliss,conf.d,ecdsa,pkcs{12,8},private,pubkey,rsa} $(INSTALL_DIR) $(1)/etc/swanctl/x509{,aa,ac,ca,crl,ocsp} $(CP) $(PKG_INSTALL_DIR)/etc/swanctl/swanctl.conf $(1)/etc/swanctl/ + echo "include /var/swanctl/swanctl.conf" >> $(1)/etc/swanctl/swanctl.conf $(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/swanctl $(1)/usr/sbin/ + $(INSTALL_BIN) ./files/swanctl.init $(1)/etc/init.d/swanctl endef define Package/strongswan-libtls/install diff --git a/net/strongswan/files/swanctl.init b/net/strongswan/files/swanctl.init new file mode 100644 index 000000000..21fc7e8ec --- /dev/null +++ b/net/strongswan/files/swanctl.init @@ -0,0 +1,601 @@ +#!/bin/sh /etc/rc.common + +START=90 +STOP=10 + +USE_PROCD=1 +PROG=/usr/lib/ipsec/starter + +. $IPKG_INSTROOT/lib/functions.sh +. $IPKG_INSTROOT/lib/functions/network.sh + +STRONGSWAN_CONF_FILE=/etc/strongswan.conf +STRONGSWAN_VAR_CONF_FILE=/var/ipsec/strongswan.conf + +SWANCTL_CONF_FILE=/etc/swanctl/swanctl.conf +SWANCTL_VAR_CONF_FILE=/var/swanctl/swanctl.conf + +WAIT_FOR_INTF=0 + +time2seconds() +{ + local timestring="$1" + local multiplier number suffix + + suffix="${timestring//[0-9 ]}" + number="${timestring%%$suffix}" + [ "$number$suffix" != "$timestring" ] && return 1 + case "$suffix" in + ""|s) + multiplier=1 ;; + m) + multiplier=60 ;; + h) + multiplier=3600 ;; + d) + multiplier=86400 ;; + *) + return 1 ;; + esac + echo $(( number * multiplier )) +} + +seconds2time() +{ + local seconds="$1" + + if [ $seconds -eq 0 ]; then + echo "0s" + elif [ $((seconds % 86400)) -eq 0 ]; then + echo "$((seconds / 86400))d" + elif [ $((seconds % 3600)) -eq 0 ]; then + echo "$((seconds / 3600))h" + elif [ $((seconds % 60)) -eq 0 ]; then + echo "$((seconds / 60))m" + else + echo "${seconds}s" + fi +} + +file_reset() { + : > "$1" +} + +xappend() { + local file="$1" + shift + + echo "$@" >> "$file" +} + +swan_reset() { + file_reset "$STRONGSWAN_VAR_CONF_FILE" +} + +swan_xappend() { + xappend "$STRONGSWAN_VAR_CONF_FILE" "$@" +} + +swan_xappend0() { + swan_xappend "$@" +} + +swan_xappend1() { + swan_xappend " ""$@" +} + +swan_xappend2() { + swan_xappend " ""$@" +} + +swan_xappend3() { + swan_xappend " ""$@" +} + +swan_xappend4() { + swan_xappend " ""$@" +} + +swanctl_reset() { + file_reset "$SWANCTL_VAR_CONF_FILE" +} + +swanctl_xappend() { + xappend "$SWANCTL_VAR_CONF_FILE" "$@" +} + +swanctl_xappend0() { + swanctl_xappend "$@" +} + +swanctl_xappend1() { + swanctl_xappend " ""$@" +} + +swanctl_xappend2() { + swanctl_xappend " ""$@" +} + +swanctl_xappend3() { + swanctl_xappend " ""$@" +} + +swanctl_xappend4() { + swanctl_xappend " ""$@" +} + +warning() { + echo "WARNING: $@" >&2 +} + +is_aead() { + local cipher="$1" + + case "$cipher" in + aes*gcm*|aes*ccm*|aes*gmac*) + return 0 ;; + esac + + return 1 +} + +add_esp_proposal() { + local encryption_algorithm + local hash_algorithm + local dh_group + + config_get encryption_algorithm "$1" encryption_algorithm + config_get hash_algorithm "$1" hash_algorithm + config_get dh_group "$1" dh_group + + # check for AEAD and clobber hash_algorithm if set + if is_aead "$encryption_algorithm" && [ -n "$hash_algorithm" ]; then + warning "Can't have $hash_algorithm with $encryption_algorithm" + hash_algorithm= + fi + + [ -n "$encryption_algorithm" ] && \ + crypto="${crypto:+${crypto},}${encryption_algorithm}${hash_algorithm:+-${hash_algorithm}}${dh_group:+-${dh_group}}" +} + +parse_esp_proposal() { + local conf="$1" + local crypto="" + + config_list_foreach "$conf" crypto_proposal add_esp_proposal + + echo "$crypto" +} + +add_ike_proposal() { + local encryption_algorithm + local hash_algorithm + local dh_group + local prf_algorithm + + config_get encryption_algorithm "$1" encryption_algorithm + config_get hash_algorithm "$1" hash_algorithm + config_get dh_group "$1" dh_group + config_get prf_algorithm "$1" prf_algorithm + + # check for AEAD and clobber hash_algorithm if set + if is_aead "$encryption_algorithm" && [ -n "$hash_algorithm" ]; then + warning "Can't have $hash_algorithm with $encryption_algorithm" + hash_algorithm= + fi + + [ -n "$encryption_algorithm" ] && \ + crypto="${crypto:+${crypto},}${encryption_algorithm}${hash_algorithm:+-${hash_algorithm}}${prf_algorithm:+-${prf_algorithm}}${dh_group:+-${dh_group}}" +} + +parse_ike_proposal() { + local conf="$1" + local crypto="" + + config_list_foreach "$conf" crypto_proposal add_ike_proposal + + echo "$crypto" +} + +config_conn() { + # Generic ipsec conn section shared by tunnel and transport + local config_name="$1" + local mode="$2" + + local local_subnet + local local_nat + local updown + local firewall + local remote_subnet + local remote_sourceip + local lifetime + local dpdaction + local closeaction + local startaction + local if_id + local rekeytime + + config_get startaction "$1" startaction "route" + config_get local_subnet "$1" local_subnet "" + config_get local_nat "$1" local_nat "" + config_get updown "$1" updown "" + config_get firewall "$1" firewall "" + config_get remote_subnet "$1" remote_subnet "" + config_get remote_sourceip "$1" remote_sourceip "" + config_get lifetime "$1" lifetime "" + config_get dpdaction "$1" dpdaction "none" + config_get closeaction "$1" closeaction "none" + config_get if_id "$1" if_id "" + config_get rekeytime "$1" rekeytime "" + + local esp_proposal="$(parse_esp_proposal "$1")" + + # translate from ipsec to swanctl + case "$startaction" in + add) + startaction="none" ;; + route) + startaction="trap" ;; + start|none|trap) + # already using new syntax + ;; + *) + warning "Startaction $startaction unknown" + startaction= + ;; + esac + + case "$closeaction" in + none|clear) + closeaction="none" ;; + hold) + closeaction="trap" ;; + restart) + closeaction="start" ;; + trap|start) + # already using new syntax + ;; + *) + warning "Closeaction $closeaction unknown" + closeaction= + ;; + esac + + [ -n "$closeaction" -a "$closeaction" != "none" ] && warning "Closeaction $closeaction can cause instability" + + case "$dpdaction" in + none) + dpddelay="0s" + dpdaction= + ;; + clear) + ;; + hold) + dpdaction="trap" ;; + restart) + dpdaction="start" ;; + trap|start) + # already using new syntax + ;; + *) + warning "Dpdaction $dpdaction unknown" + dpdaction= + ;; + esac + + [ -n "$local_nat" ] && local_subnet="$local_nat" + + swanctl_xappend3 "$config_name {" + + [ -n "$local_subnet" ] && swanctl_xappend4 "local_ts = $local_subnet" + [ -n "$remote_subnet" ] && swanctl_xappend4 "remote_ts = $remote_subnet" + [ -n "$if_id" ] && { swanctl_xappend4 "if_id_in = $if_id" ; swanctl_xappend4 "if_id_out = $if_id" ; } + [ -n "$startaction" -a "$startaction" != "none" ] && swanctl_xappend4 "start_action = $startaction" + [ -n "$closeaction" -a "$closeaction" != "none" ] && swanctl_xappend4 "close_action = $closeaction" + swanctl_xappend4 "esp_proposals = $esp_proposal" + swanctl_xappend4 "mode = $mode" + + if [ -n "$lifetime" ]; then + swanctl_xappend4 "life_time = $lifetime" + elif [ -n "$rekeytime" ]; then + swanctl_xappend4 "life_time = $(seconds2time $(((110 * $(time2seconds $rekeytime)) / 100)))" + fi + [ -n "$rekeytime" ] && swanctl_xappend4 "rekey_time = $rekeytime" + + [ -n "$updown" ] && swanctl_xappend4 "updown = $updown" + [ -n "$dpdaction" ] && swanctl_xappend4 "dpd_action = $dpdaction" + + swanctl_xappend3 "}" +} + +config_tunnel() { + config_conn "$1" "tunnel" +} + +config_transport() { + config_conn "$1" "transport" +} + +config_remote() { + local config_name="$1" + + local enabled + local gateway + local local_gateway + local local_sourceip + local local_leftip + local remote_gateway + local pre_shared_key + local auth_method + local keyingtries + local dpddelay + local inactivity + local keyexchange + local reqid + local packet_marker + local fragmentation + local mobike + local local_cert + local local_key + local ca_cert + local rekeytime + + config_get_bool enabled "$1" enabled 0 + [ $enabled -eq 0 ] && return + + config_get gateway "$1" gateway + config_get pre_shared_key "$1" pre_shared_key + config_get auth_method "$1" authentication_method + config_get local_identifier "$1" local_identifier "" + config_get remote_identifier "$1" remote_identifier "" + config_get local_sourceip "$1" local_sourceip "" + config_get local_leftip "$1" local_leftip "%any" + config_get keyingtries "$1" keyingtries "3" + config_get dpddelay "$1" dpddelay "30s" + config_get inactivity "$1" inactivity + config_get keyexchange "$1" keyexchange "ikev2" + config_get reqid "$1" reqid + config_get packet_marker "$1" packet_marker + config_get fragmentation "$1" fragmentation "yes" + config_get_bool mobike "$1" mobike 1 + config_get local_cert "$1" local_cert "" + config_get local_key "$1" local_key "" + config_get ca_cert "$1" ca_cert "" + config_get rekeytime "$1" rekeytime + config_get overtime "$1" overtime + + case "$fragmentation" in + 0) + fragmentation="no" ;; + 1) + fragmentation="yes" ;; + yes|accept|force|no) + # already using new syntax + ;; + *) + warning "Fragmentation $fragmentation not supported" + fragmentation= + ;; + esac + + [ "$gateway" = "any" ] && remote_gateway="%any" || remote_gateway="$gateway" + + [ -z "$local_gateway" ] && { + local ipdest + + [ "$remote_gateway" = "%any" ] && ipdest="1.1.1.1" || ipdest="$remote_gateway" + local_gateway=`ip -o route get $ipdest | awk '/ src / { gsub(/^.* src /,""); gsub(/ .*$/, ""); print $0}'` + } + + local ike_proposal="$(parse_ike_proposal "$1")" + + [ -n "$firewall" ] && warning "Firewall not supported" + + swanctl_xappend0 "# config for $config_name" + swanctl_xappend0 "connections {" + swanctl_xappend1 "$config_name {" + swanctl_xappend2 "local_addrs = $local_leftip" + swanctl_xappend2 "remote_addrs = $remote_gateway" + + [ -n "$local_sourceip" ] && swanctl_xappend2 "vips = $local_sourceip" + [ -n "$fragmentation" ] && swanctl_xappend2 "fragmentation = $fragmentation" + + swanctl_xappend2 "local {" + swanctl_xappend3 "auth = $auth_method" + + [ -n "$local_identifier" ] && swanctl_xappend3 "id = \"$local_identifier\"" + [ "$auth_method" = pubkey ] && swanctl_xappend3 "certs = $local_cert" + swanctl_xappend2 "}" + + swanctl_xappend2 "remote {" + swanctl_xappend3 "auth = $auth_method" + [ -n "$remote_identifier" ] && swanctl_xappend3 "id = \"$remote_identifier\"" + swanctl_xappend2 "}" + + swanctl_xappend2 "children {" + + config_list_foreach "$1" tunnel config_tunnel + + config_list_foreach "$1" transport config_transport + + swanctl_xappend2 "}" + + case "$keyexchange" in + ike) + ;; + ikev1) + swanctl_xappend2 "version = 1" ;; + ikev2) + swanctl_xappend2 "version = 2" ;; + *) + warning "Keyexchange $keyexchange not supported" + keyexchange= + ;; + esac + + [ $mobike -eq 1 ] && swanctl_xappend2 "mobike = yes" || swanctl_xappend2 "mobike = no" + + if [ -n "$rekeytime" ]; then + swanctl_xappend2 "rekey_time = $rekeytime" + + if [ -z "$overtime" ]; then + overtime=$(seconds2time $(($(time2seconds $rekeytime) / 10))) + fi + fi + [ -n "$overtime" ] && swanctl_xappend2 "over_time = $overtime" + + swanctl_xappend2 "proposals = $ike_proposal" + [ -n "$dpddelay" ] && swanctl_xappend2 "dpd_delay = $dpddelay" + [ "$keyingtries" = "%forever" ] && swanctl_xappend2 "keyingtries = 0" || swanctl_xappend2 "keyingtries = $keyingtries" + + swanctl_xappend1 "}" + swanctl_xappend0 "}" + + if [ "$auth_method" = pubkey ]; then + swanctl_xappend0 "" + + swanctl_xappend0 "secrets {" + swanctl_xappend1 "rsa {" + swanctl_xappend2 "filename = $local_key" + swanctl_xappend1 "}" + swanctl_xappend0 "}" + + swanctl_xappend0 "" + + if [ -n "$ca_cert" ]; then + swanctl_xappend0 "authorities {" + swanctl_xappend1 "$config_name {" + swanctl_xappend2 "cacert = $ca_cert" + swanctl_xappend1 "}" + swanctl_xappend0 "}" + fi + + elif [ "$auth_method" = psk ]; then + swanctl_xappend0 "" + + swanctl_xappend0 "secrets {" + swanctl_xappend1 "ike {" + swanctl_xappend2 "secret = $pre_shared_key" + if [ -z "$local_id" ]; then + swanctl_xappend2 "id1 = $local_id" + if [ -z "$remote_id" ]; then + swanctl_xappend2 "id2 = $remote_id" + fi + fi + else + warning "AuthenticationMode $auth_mode not supported" + fi + + swanctl_xappend0 "" +} + +do_preamble() { + swanctl_xappend0 "# generated by /etc/init.d/swanctl" +} + +config_ipsec() { + local debug + local rtinstall_enabled + local routing_tables_ignored + local routing_table + local routing_table_id + local interface + local device_list + + swan_reset + swanctl_reset + do_preamble + + config_get debug "$1" debug 0 + config_get_bool rtinstall_enabled "$1" rtinstall_enabled 1 + [ $rtinstall_enabled -eq 1 ] && install_routes=yes || install_routes=no + + # prepare extra charon config option ignore_routing_tables + for routing_table in $(config_get "$1" "ignore_routing_tables"); do + if [ "$routing_table" -ge 0 ] 2>/dev/null; then + routing_table_id=$routing_table + else + routing_table_id=$(sed -n '/[ \t]*[0-9]\+[ \t]\+'$routing_table'[ \t]*$/s/[ \t]*\([0-9]\+\).*/\1/p' /etc/iproute2/rt_tables) + fi + + [ -n "$routing_table_id" ] && append routing_tables_ignored "$routing_table_id" + done + + local interface_list=$(config_get "$1" "interface") + if [ -z "$interface_list" ]; then + WAIT_FOR_INTF=0 + else + for interface in $interface_list; do + network_get_device device $interface + [ -n "$device" ] && append device_list "$device" "," + done + [ -n "$device_list" ] && WAIT_FOR_INTF=0 || WAIT_FOR_INTF=1 + fi + + swan_xappend0 "# generated by /etc/init.d/swanctl" + swan_xappend0 "charon {" + swan_xappend1 "install_routes = $install_routes" + [ -n "$routing_tables_ignored" ] && swan_xappend1 "ignore_routing_tables = $routing_tables_ignored" + [ -n "$device_list" ] && swan_xappend1 "interfaces_use = $device_list" + swan_xappend1 "start-scripts {" + swan_xappend2 "load-all = /usr/sbin/swanctl --load-all --noprompt" + swan_xappend1 "}" + swan_xappend1 "syslog {" + swan_xappend2 "identifier = ipsec" + swan_xappend2 "daemon {" + swan_xappend3 "default = $debug" + swan_xappend2 "}" + swan_xappend1 "}" + swan_xappend0 "}" +} + +prepare_env() { + mkdir -p /var/ipsec /var/swanctl + config_load ipsec + config_foreach config_ipsec ipsec + config_foreach config_remote remote +} + +service_running() { + swanctl --stats > /dev/null 2>&1 +} + +reload_service() { + running && { + prepare_env + [ $WAIT_FOR_INTF -eq 0 ] && { + swanctl --load-all --noprompt + return + } + } + + start +} + +stop_service() { + swan_reset + swanctl_reset +} + +service_triggers() { + procd_add_reload_trigger "ipsec" + config load "ipsec" +} + +start_service() { + prepare_env + + [ $WAIT_FOR_INTF -eq 1 ] && return + + procd_open_instance + + procd_set_param command $PROG --daemon charon --nofork + + procd_set_param file $SWANCTL_CONF_FILE + procd_append_param file /etc/swanctl/conf.d/*.conf + procd_append_param file $STRONGSWAN_CONF_FILE + + procd_set_param respawn + + procd_close_instance +}