#!/bin/sh /etc/rc.common # Copyright (c) 2012-2016, Matthias Schiffer START=95 SERVICE_USE_PID=1 EXTRA_COMMANDS="up down show_key generate_key" LIST_SEP=" " TMP_FASTD=/tmp/fastd FASTD_COMMAND=/usr/bin/fastd section_enabled() { config_get_bool enabled "$1" 'enabled' 0 [ $enabled -gt 0 ] } error() { echo "${initscript}:" "$@" 1>&2 } get_key_instance() { local s="$1" config_get secret "$s" secret if [ "$secret" = 'generate' ]; then secret=`"$FASTD_COMMAND" --generate-key --machine-readable` uci -q set fastd."$s".secret="$secret" && uci -q commit fastd fi echo "$secret" } escape_string() { local t=${1//\\/\\\\} echo -n "\"${t//\"/\\\"}\"" } guard_value() { local t=${1//[^-a-z0-9\[\].:]/} echo -n "$t" } guard_quotes() { local t=${1//[^-a-zA-Z0-9\[\].:\"% ]/} local quotes=${t//[^\"]/} if [ "${#quotes}" = 0 -o "${#quotes}" = 2 ]; then echo -n "$t" fi } yes_no() { case "$1" in 0|no|off|false|disabled) echo -n no;; *) echo -n yes;; esac } config_string_config='include $(escape_string "$value");' config_string_config_peer='include peer $(escape_string "$value");' config_string_config_peer_dir='include peers from $(escape_string "$value");' config_string_bind='bind $(guard_quotes "$value");' config_string_method='method $(escape_string "$value");' config_string_syslog_level='log to syslog level $(guard_value "$value");' config_string_mode='mode $(guard_value "$value");' config_string_interface='interface $(escape_string "$value");' config_string_mtu='mtu $(guard_value "$value");' config_string_peer_limit='peer limit $(guard_value "$value");' config_string_user='user $(escape_string "$value");' config_string_group='group $(escape_string "$value");' config_string_status_socket='status socket $(escape_string "$value");' config_string_forward='forward $(yes_no "$value");' config_string_hide_ip_addresses='hide ip addresses $(yes_no "$value");' config_string_hide_mac_addresses='hide mac addresses $(yes_no "$value");' config_string_secure_handshakes='secure handshakes $(yes_no "$value");' config_string_packet_mark='packet mark $(guard_value "$value");' config_string_on_pre_up='on pre-up $(escape_string "$value");' config_string_on_up='on up $(escape_string "$value");' config_string_on_down='on down $(escape_string "$value");' config_string_on_post_down='on post-down $(escape_string "$value");' config_string_on_connect='on connect $(escape_string "$value");' config_string_on_establish='on establish $(escape_string "$value");' config_string_on_disestablish='on disestablish $(escape_string "$value");' config_string_on_verify='on verify $(escape_string "$value");' config_string_peer='peer $(escape_string "$value") {' config_string_peer_group='peer group $(escape_string "$value") {' peer_string_key='key $(escape_string "$value");' peer_string_float='float $(yes_no "$value");' peer_string_remote='remote $(guard_quotes "$value");' generate_option() { local __string=$(eval echo \"\$$2\") local value="$1"; eval echo "\"$__string\"" } append_option() { local v; local len; local s="$1"; local prefix="$2"; local p="$3" config_get len "$s" "${p}_LENGTH" if [ -z "$len" ]; then config_get v "$s" "$p" [ -n "$v" ] && generate_option "$v" "${prefix}_string_${p}" else config_list_foreach "$s" "$p" generate_option "${prefix}_string_${p}" fi } append_options() { local p; local s="$1"; local prefix="$2"; shift; shift for p in $*; do append_option "$s" "$prefix" "$p" done } generate_config_secret() { echo "secret $(escape_string "$1");" } generate_peer_config() { local peer="$1" # These options are deprecated config_get address "$peer" address config_get hostname "$peer" hostname config_get address_family "$peer" address_family config_get port "$peer" port if [ "$address" -o "$hostname" ]; then if [ -z "$port" ]; then error "peer $peer: address or hostname, but no port given" return 1 fi if [ "$hostname" ]; then generate_option peer_string_remote "$address_family \"$hostname\" port $port" fi if [ "$address" ]; then generate_option peer_string_remote "$address port $port" fi fi append_options "$peer" peer \ key float remote } generate_single_peer_config() { local peer="$1"; local net="$2" config_get peer_net "$peer" net config_get peer_group "$peer" group [ "$net" = "$peer_net" -a "$peer_group" = '' ] || return 0 section_enabled "$peer" || return 0 generate_option "$peer" config_string_peer generate_peer_config "$peer" echo '}' } create_peer_config() { local peer="$1"; local net="$2"; local group="$3"; local path="$4" config_get peer_net "$peer" net config_get peer_group "$peer" group [ "$group" = "$peer_group" ] || return 0 if [ "$net" != "$peer_net" ]; then [ -z "$group" ] || error "warning: the peer group of peer '$peer' doesn't match its net, the peer will be ignored" return 0 fi section_enabled "$peer" || return 0 generate_peer_config "$peer" >"$path/$peer" } update_peer_group() { local net="$1"; local group_dir="$2"; local group="$3"; local update_only="$4" local path="$TMP_FASTD/fastd.$net/$group_dir" rm -rf "$path" mkdir -p "$path" config_foreach create_peer_config 'peer' "$net" "$group" "$path" if [ -z "$update_only" ]; then generate_option "$path" config_string_config_peer_dir fi config_foreach generate_peer_group_config 'peer_group' "$net" "$group_dir" "$update_only" "$group" } generate_peer_group_config() { local group="$1"; local net="$2"; local group_dir="$3%$group"; local update_only="$4"; local parent="$5" config_get group_net "$group" net config_get group_parent "$group" parent [ "$parent" = "$group_parent" ] || return 0 if [ "$net" != "$peer_net" ]; then [ -z "$parent" ] || error "warning: the parent of peer group '$group' doesn't match its net, the peer group will be ignored" return 0 fi section_enabled "$group" || return 0 if [ -z "$update_only" ]; then generate_option "$group" config_string_peer_group append_options "$group" config \ config config_peer config_peer_dir method peer_limit fi update_peer_group "$net" "$group_dir" "$group" "$update_only" if [ -z "$update_only" ]; then echo '}' fi } update_peer_groups() { local net="$1"; local update_only="$2" update_peer_group "$net" 'peers' '' "$update_only" } generate_config() { local s="$1" generate_option 'info' config_string_syslog_level append_options "$s" config \ config config_peer config_peer_dir bind method syslog_level mode interface mtu peer_limit \ user group status_socket forward hide_ip_addresses hide_mac_addresses secure_handshakes packet_mark \ on_pre_up on_up on_down on_post_down on_connect on_establish on_disestablish on_verify config_get mode "$s" mode if [ "$mode" = "tun" ]; then config_foreach generate_single_peer_config 'peer' "$s" else update_peer_groups "$s" fi } generate_key_instance() { local s="$1" config_get secret "$s" secret if [ -z "$secret" -o "$secret" = 'generate' ]; then secret=`fastd --generate-key --machine-readable` uci -q set fastd."$s".secret="$secret" && uci -q commit fastd fi generate_config_secret "$secret" | "$FASTD_COMMAND" --config - --show-key --machine-readable } show_key_instance() { local s="$1" local secret=`get_key_instance "$s"` if [ -z "$secret" ]; then error "$s: secret is not set" return 1 fi generate_config_secret "$secret" | "$FASTD_COMMAND" --config - --show-key --machine-readable } start_instance() { local s="$1" section_enabled "$s" || return 1 SERVICE_PID_FILE="/var/run/fastd.$s.pid" config_get interface "$s" interface if [ -z "$interface" ]; then error "$s: interface is not set" return 1 fi if ifconfig "$interface" &>/dev/null; then error "$s: interface '$interface' is already in use" return 1 fi config_get mode "$s" mode if [ -z "$mode" ]; then error "$s: mode is not set" return 1 fi local secret=`get_key_instance "$s"` if [ -z "$secret" ]; then error "$s: secret is not set" return 1 fi rm -f "$SERVICE_PID_FILE" touch "$SERVICE_PID_FILE" config_get user "$s" user if [ "$user" ]; then chown "$user" "$SERVICE_PID_FILE" fi (generate_config_secret "$secret"; generate_config "$s") | service_start "$FASTD_COMMAND" --config - --daemon --pid-file "$SERVICE_PID_FILE" if ! ifconfig "$interface" >/dev/null 2>&1; then error "$s: startup failed" return 1 fi config_get up "$s" up [ -n "$up" ] && sh -c "$up" - "$interface" } stop_instance() { local s="$1" section_enabled "$s" || return 1 SERVICE_PID_FILE="/var/run/fastd.$s.pid" config_get interface "$s" interface if [ -z "$interface" ]; then error "$s: interface is not set" return 1 fi if ! ifconfig "$interface" &>/dev/null; then error "$s: interface '$interface' does not exist" return 1 fi config_get down "$s" down [ -n "$down" ] && sh -c "$down" - "$interface" service_stop "$FASTD_COMMAND" rm -rf "$TMP_FASTD/fastd.$s" } reload_instance() { local s="$1" section_enabled "$s" || return 1 config_get mode "$s" mode [ "$mode" = "tun" ] && return 1 update_peer_groups "$s" true SERVICE_PID_FILE="/var/run/fastd.$s.pid" service_reload "$FASTD_COMMAND" } start() { config_load 'fastd' config_foreach start_instance 'fastd' return 0 } stop() { config_load 'fastd' config_foreach stop_instance 'fastd' return 0 } reload() { config_load 'fastd' config_foreach reload_instance 'fastd' return 0 } up() { local exists local instance config_load 'fastd' for instance in "$@"; do config_get exists "$instance" 'TYPE' if [ "$exists" = 'fastd' ]; then start_instance "$instance" fi done } down() { local exists local instance config_load 'fastd' for instance in "$@"; do config_get exists "$instance" 'TYPE' if [ "$exists" = 'fastd' ]; then stop_instance "$instance" fi done } show_key() { local exists local instance config_load 'fastd' for instance in "$@"; do config_get exists "$instance" 'TYPE' if [ "$exists" = 'fastd' ]; then show_key_instance "$instance" fi done } generate_key() { local exists local instance config_load 'fastd' for instance in "$@"; do config_get exists "$instance" 'TYPE' if [ "$exists" = 'fastd' ]; then generate_key_instance "$instance" fi done }