ipsec uses starter, and reads /etc/ipsec.conf (which then includes /var/ipsec/ipsec.conf, etc). This is overly complicated, and can be problematic if you're using both swanctl and ipsec for migration. Running charon directly from procd via the init.d script avoid all of this. Signed-off-by: Philip Prindeville <philipp@redfish-solutions.com>
626 lines
14 KiB
Bash
626 lines
14 KiB
Bash
#!/bin/sh /etc/rc.common
|
|
|
|
START=90
|
|
STOP=10
|
|
|
|
USE_PROCD=1
|
|
PROG=/usr/lib/ipsec/charon
|
|
|
|
. $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
|
|
|
|
CONFIG_FAIL=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
|
|
}
|
|
|
|
fatal() {
|
|
echo "ERROR: $@" >&2
|
|
CONFIG_FAIL=1
|
|
}
|
|
|
|
is_aead() {
|
|
local cipher="$1"
|
|
|
|
case "$cipher" in
|
|
aes*gcm*|aes*ccm*|aes*gmac*)
|
|
return 0 ;;
|
|
chacha20poly1305)
|
|
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
|
|
fatal "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 var="$2"
|
|
|
|
local crypto=""
|
|
|
|
config_list_foreach "$conf" crypto_proposal add_esp_proposal
|
|
|
|
export -n "$var=$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
|
|
fatal "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 var="$2"
|
|
|
|
local crypto=""
|
|
|
|
config_list_foreach "$conf" crypto_proposal add_ike_proposal
|
|
|
|
export -n "$var=$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" esp_proposal
|
|
|
|
# translate from ipsec to swanctl
|
|
case "$startaction" in
|
|
add)
|
|
startaction="none" ;;
|
|
route)
|
|
startaction="trap" ;;
|
|
start|none|trap)
|
|
# already using new syntax
|
|
;;
|
|
*)
|
|
fatal "Startaction $startaction unknown"
|
|
startaction=
|
|
;;
|
|
esac
|
|
|
|
case "$closeaction" in
|
|
none|clear)
|
|
closeaction="none" ;;
|
|
hold)
|
|
closeaction="trap" ;;
|
|
restart)
|
|
closeaction="start" ;;
|
|
trap|start)
|
|
# already using new syntax
|
|
;;
|
|
*)
|
|
fatal "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
|
|
;;
|
|
*)
|
|
fatal "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
|
|
;;
|
|
*)
|
|
fatal "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" ike_proposal
|
|
|
|
[ -n "$firewall" ] && fatal "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" ;;
|
|
*)
|
|
fatal "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
|
|
fatal "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
|
|
|
|
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
|
|
}
|
|
|
|
do_postamble() {
|
|
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
|
|
|
|
swan_reset
|
|
swanctl_reset
|
|
do_preamble
|
|
|
|
config_load ipsec
|
|
config_foreach config_ipsec ipsec
|
|
config_foreach config_remote remote
|
|
|
|
do_postamble
|
|
}
|
|
|
|
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
|
|
|
|
if [ $CONFIG_FAIL -ne 0 ]; then
|
|
procd_set_param error "Invalid configuration"
|
|
return
|
|
fi
|
|
|
|
procd_open_instance
|
|
|
|
procd_set_param command $PROG
|
|
|
|
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
|
|
}
|