#! /bin/sh # iproute2 version, default updown script # # Copyright (C) 2003-2004 Nigel Metheringham # Copyright (C) 2002-2007 Michael Richardson # Copyright (C) 2003-2013 Tuomo Soini # Copyright (C) 2008 Paul Wouters # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the # Free Software Foundation; either version 2 of the License, or (at your # option) any later version. See . # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License # for more details. # CAUTION: Installing a new version of Libreswan will install a new # copy of this script, wiping out any custom changes you make. If # you need changes, make a copy of this under another name, and customize # that, and use the (left/right)updown= parameters in ipsec.conf to make # Libreswan use yours instead of this default one. test ${IPSEC_INIT_SCRIPT_DEBUG} && set -v -x LC_ALL=C export LC_ALL # things that this script gets (from ipsec_pluto(8) man page) # # # PLUTO_VERSION # indicates what version of this interface is being # used. This document describes version 1.1. This # is upwardly compatible with version 1.0. # # PLUTO_VERB # specifies the name of the operation to be performed # (prepare-host, prepare-client, up-host, up-client, # down-host, or down-client). If the address family # for security gateway to security gateway communications # is IPv6, then a suffix of -v6 is added to the # verb. # # PLUTO_CONNECTION # is the name of the connection for which we are # routing. # # PLUTO_CONN_POLICY # the policy of the connection, as in: # RSASIG+ENCRYPT+TUNNEL+PFS+DONTREKEY+OPPORTUNISTIC # +failureDROP+lKOD+rKOD # # PLUTO_NEXT_HOP # is the next hop to which packets bound for the peer # must be sent. # # PLUTO_INTERFACE # is the name of the ipsec interface to be used. # # PLUTO_ME # is the IP address of our host. # # PLUTO_METRIC # is the metric to set for the route # # PLUTO_MTU # is the mtu to set for the route # # PLUTO_MY_CLIENT # is the IP address / count of our client subnet. If # the client is just the host, this will be the # host's own IP address / max (where max is 32 for # IPv4 and 128 for IPv6). # # PLUTO_MY_CLIENT_NET # is the IP address of our client net. If the client # is just the host, this will be the host's own IP # address. # # PLUTO_MY_CLIENT_MASK # is the mask for our client net. If the client is # just the host, this will be 255.255.255.255. # # PLUTO_MY_SOURCEIP # if non-empty, then the source address for the route will be # set to this IP address. # # PLUTO_MY_PROTOCOL # is the protocol for this connection. Useful for # firewalling. # # PLUTO_MY_PORT # is the port. Useful for firewalling. # # PLUTO_PEER # is the IP address of our peer. # # PLUTO_PEER_CLIENT # is the IP address / count of the peer's client subnet. # If the client is just the peer, this will be # the peer's own IP address / max (where max is 32 # for IPv4 and 128 for IPv6). # # PLUTO_PEER_CLIENT_NET # is the IP address of the peer's client net. If the # client is just the peer, this will be the peer's # own IP address. # # PLUTO_PEER_CLIENT_MASK # is the mask for the peer's client net. If the # client is just the peer, this will be # 255.255.255.255. # # PLUTO_PEER_PROTOCOL # is the protocol set for remote end with port # selector. # # PLUTO_PEER_PORT # is the peer's port. Useful for firewalling. # # PLUTO_CONNECTION_TYPE # # PLUTO_CONN_ADDRFAMILY # is the family type, "ipv4" or "ipv6" # # PLUTO_STACK # IPsec stack used by pluto (eg protostack= values) # # PLUTO_NM_CONFIGURED # is NetworkManager used for resolv.conf update # # PLUTO_SAREF_TRACKING # If we need to manipulate any iptables for SAref tracking # # PLUTO_SA_REQID # When using KAME or XFRM/NETKEY, the IPsec SA reqid value # for debugging of the script #exec >/tmp/_updown.m$$ #exec 2>&1 #set -x # Import default _updown configs from the @FINALSYSCONFDIR@/[sysconfig|default]pluto_updown file # # Two variables can be set in this file: # # DEFAULTSOURCE # is the default value for PLUTO_MY_SOURCEIP # # IPROUTEARGS # is the extra argument list for ip route command # # IPRULEARGS # is the extra argument list for ip rule command # # rpm based systems if [ -f @FINALSYSCONFDIR@/sysconfig/pluto_updown ]; then . @FINALSYSCONFDIR@/sysconfig/pluto_updown # deb based systems elif [ -f @FINALSYSCONFDIR@/default/pluto_updown ]; then . @FINALSYSCONFDIR@/default/pluto_updown fi LIBRESWAN_RESOLV_CONF=@FINALVARDIR@/run/pluto/libreswan-resolv-conf-backup ORIG_RESOLV_CONF=@FINALSYSCONFDIR@/resolv.conf use_comment=true # check parameter(s) case "${1}:$*" in ':') # no parameters ;; custom:*) # custom parameters (see above CAUTION comment) ;; *) echo "${0}: unknown parameters \"$*\"" >&2 exit 2 ;; esac # function to see if iptables has an IPSEC chain already, and if not, # it will create one and insert it into the OUTPUT and FORWARD chains. checkipsec() { if [ -z "${PLUTO_SAREF_TRACKING}" -o "${PLUTO_SAREF_TRACKING}" = "no" ]; then echo "SAref table initialisation left to third party" elif [ -z "${PLUTO_CONN_ADDRFAMILY}" -o "${PLUTO_CONN_ADDRFAMILY}" = "ipv6" ]; then echo "SAref not activated for IPv6" else # make sure we have an IPSEC chain if ! ( iptables -n -t mangle -L IPSEC >/dev/null 2>&1); then iptables -t mangle -N IPSEC fi if ! ( ip6tables -n -t mangle -L IPSEC >/dev/null 2>&1); then ip6tables -t mangle -N IPSEC fi # we also need to have a NEW_IPSEC_CONN chain if ! ( iptables -n -t mangle -L NEW_IPSEC_CONN >/dev/null 2>&1); then iptables -t mangle -N NEW_IPSEC_CONN fi if ! ( ip6tables -n -t mangle -L NEW_IPSEC_CONN >/dev/null 2>&1); then ip6tables -t mangle -N NEW_IPSEC_CONN fi # initialize the IPSEC chain if needed if [ "${PLUTO_SAREF_TRACKING}" = "conntrack" ]; then if [ "$(iptables -n -t mangle -L IPSEC | wc -l )" != 6 ]; then iptables -t mangle -F IPSEC iptables -t mangle -A IPSEC -j CONNMARK --restore-mark iptables -t mangle -A IPSEC -m mark --mark 0x80000000/0x80000000 -j RETURN iptables -t mangle -A IPSEC -j NEW_IPSEC_CONN iptables -t mangle -A IPSEC -j CONNMARK --save-mark fi if [ "$(ip6tables -n -t mangle -L IPSEC | wc -l )" != 6 ]; then ip6tables -t mangle -F IPSEC ip6tables -t mangle -A IPSEC -j CONNMARK --restore-mark ip6tables -t mangle -A IPSEC -m mark --mark 0x80000000/0x80000000 -j RETURN ip6tables -t mangle -A IPSEC -j NEW_IPSEC_CONN ip6tables -t mangle -A IPSEC -j CONNMARK --save-mark fi else if [ "$(iptables -n -t mangle -L IPSEC | wc -l )" != 3 ]; then iptables -t mangle -F IPSEC iptables -t mangle -A IPSEC -j NEW_IPSEC_CONN fi if [ "$(ip6tables -n -t mangle -L IPSEC | wc -l )" != 3 ]; then ip6tables -t mangle -F IPSEC ip6tables -t mangle -A IPSEC -j NEW_IPSEC_CONN fi fi # next, setup routing rules and tables: # we pick table 50, cause proto50=ESP if ! ( ip rule show | grep -qF 'from all fwmark 0x80000000/0x80000000 lookup 50' ); then # note "fwmarkmask" is an (obsolete) Libreswan patch to "ip" command. # note2: iproute2-2.6.22-070710 supports mask via /mask notation instead # ip rule add fwmark 0x80000000 fwmarkmask 0x80000000 table 50 ip rule add from all fwmark 0x80000000/0x80000000 lookup 50 # This rule makes sure that packets originating from mast0 (ones that came # out of a tunnel) go directly to the main table. This makes sure that # rp_filter can find the right reverse path route. ip rule add from all iif ${PLUTO_INTERFACE} lookup main fi # the default route goes over the mast interface if ! ( ip route show table 50 | grep -qF "default dev ${PLUTO_INTERFACE}" ); then ip route add default dev ${PLUTO_INTERFACE} table 50 fi # now look for -j IPSEC in OUTPUT chains. if ! (iptables -n -t mangle -L OUTPUT | grep -q '^IPSEC'); then iptables -t mangle -I OUTPUT 1 -j IPSEC iptables -t mangle -I OUTPUT 1 -p udp --sport 500 -j ACCEPT iptables -t mangle -I OUTPUT 1 -p udp --dport 500 -j ACCEPT iptables -t mangle -I OUTPUT 1 -p udp --sport 4500 -j ACCEPT iptables -t mangle -I OUTPUT 1 -p udp --dport 4500 -j ACCEPT fi if ! (ip6tables -n -t mangle -L OUTPUT | grep -q '^IPSEC'); then ip6tables -t mangle -I OUTPUT 1 -j IPSEC ip6tables -t mangle -I OUTPUT 1 -p udp --sport 500 -j ACCEPT ip6tables -t mangle -I OUTPUT 1 -p udp --dport 500 -j ACCEPT ip6tables -t mangle -I OUTPUT 1 -p udp --sport 4500 -j ACCEPT ip6tables -t mangle -I OUTPUT 1 -p udp --dport 4500 -j ACCEPT fi # now look for -j IPSEC in PREROUTING chains. if ! (iptables -n -t mangle -L PREROUTING | grep -q '^IPSEC'); then iptables -t mangle -I PREROUTING 1 -j IPSEC fi if ! (ip6tables -n -t mangle -L PREROUTING | grep -q '^IPSEC'); then ip6tables -t mangle -I PREROUTING 1 -j IPSEC fi if [ -w /proc/sys/net/ipv4/conf/${PLUTO_INTERFACE}/src_valid_mark ]; then # finally make sure that the top bit of source vmark for mast0 is set vmark=$(cat /proc/sys/net/ipv4/conf/${PLUTO_INTERFACE}/src_valid_mark) vmark=$(( ${vmark} | 0x80000000 )) echo "${vmark}" > /proc/sys/net/ipv4/conf/${PLUTO_INTERFACE}/src_valid_mark else # In case that we don't have the means to get rp_filter to cooperate # with KLIPS nfmark based routing scheme, we disable rp_filter. for n in /proc/sys/net/ipv4/conf/*/rp_filter; do echo 0 > ${n} done fi fi } # utility functions for route manipulation # called to add appropriate "erout'ing" uproute() { checkipsec doipsecrule add ip route flush cache } # called to remove any routing downroute() { checkipsec doipsecrule delete ip route flush cache } # called XXXX uprule() { doipsecrule delete doipsecrule add ip route flush cache } # called XXXX downrule() { doipsecrule delete } updateresolvconf() { if [ -z "${PLUTO_PEER_DNS_INFO}" -o -z "${PLUTO_PEER_DOMAIN_INFO}" ]; then return 0 fi if [ -n "$(pidof unbound)" ]; then echo "updating local nameserver for ${PLUTO_PEER_DOMAIN_INFO} with ${PLUTO_PEER_DNS_INFO}" /usr/sbin/unbound-control forward_add ${PLUTO_PEER_DOMAIN_INFO} ${PLUTO_PEER_DNS_INFO} /usr/sbin/unbound-control flush_zone ${PLUTO_PEER_DOMAIN_INFO} /usr/sbin/unbound-control flush_requestlist return 0 fi if [ -z "${PLUTO_NM_CONFIGURED}" -o "${PLUTO_NM_CONFIGURED}" = 0 ]; then echo "updating resolvconf" if [ ! -e "${ORIG_RESOLV_CONF}" ]; then echo "resolv.conf does not exist, so doing nothing" return 1 fi if [ -e "${LIBRESWAN_RESOLV_CONF}" ]; then if grep 'Libreswan' ${ORIG_RESOLV_CONF} > /dev/null 2>&1; then echo "Current resolv.conf is generated by Libreswan, and backup resolv.conf already exists, so doing nothing" return 1 else echo "backup resolv.conf exists, but current resolv.conf is not generated by Libreswan" fi fi rm -f ${LIBRESWAN_RESOLV_CONF} cp -- ${ORIG_RESOLV_CONF} ${LIBRESWAN_RESOLV_CONF} RESOLVE_CONF="# Generated by Libreswan (IPSec)" if [ -n "${PLUTO_PEER_DOMAIN_INFO}" ]; then if grep 'domain' ${ORIG_RESOLV_CONF} > /dev/null 2>&1; then RESOLVE_CONF="${RESOLVE_CONF}\ndomain ${PLUTO_PEER_DOMAIN_INFO}\nsearch ${PLUTO_PEER_DOMAIN_INFO}" else RESOLVE_CONF="${RESOLVE_CONF}\nsearch ${PLUTO_PEER_DOMAIN_INFO}" fi fi if [ -n "${PLUTO_PEER_DNS_INFO}" ]; then for i in ${PLUTO_PEER_DNS_INFO}; do RESOLVE_CONF="${RESOLVE_CONF}\nnameserver $i" done fi ORIG_NAMESERVER=$(grep -m 1 ^nameserver ${ORIG_RESOLV_CONF}) RESOLVE_CONF="${RESOLVE_CONF}\n${ORIG_NAMESERVER}\n" rm -f -- ${ORIG_RESOLV_CONF} printf "${RESOLVE_CONF}" > ${ORIG_RESOLV_CONF} return $? else echo "Updating resolv.conf is controlled by Network Manager" libreswan_reason=connect export libreswan_reason export PLUTO_PEER_DOMAIN_INFO export PLUTO_PEER_DNS_INFO export PLUTO_PEER_BANNER export PLUTO_MY_SOURCEIP export PLUTO_PEER /usr/libexec/nm-libreswan-service-helper return 0 fi } restoreresolvconf() { if [ -z "${PLUTO_PEER_DNS_INFO}" -o -z "${PLUTO_PEER_DOMAIN_INFO}" ]; then return 0 fi if [ -n "$(pidof unbound)" ]; then echo "flushing local nameserver of ${PLUTO_PEER_DOMAIN_INFO}" /usr/sbin/unbound-control forward_remove ${PLUTO_PEER_DOMAIN_INFO} /usr/sbin/unbound-control flush_zone ${PLUTO_PEER_DOMAIN_INFO} /usr/sbin/unbound-control flush_requestlist return 0 fi if [ -z "${PLUTO_NM_CONFIGURED}" -o "${PLUTO_NM_CONFIGURED}" = 0 ]; then echo "restoring resolvconf" if [ ! -e "${LIBRESWAN_RESOLV_CONF}" ]; then echo "Problem in restoring the resolv.conf, as there is no backup file" return 2 fi if grep 'Libreswan' ${ORIG_RESOLV_CONF} > /dev/null 2>&1; then cp -- "${LIBRESWAN_RESOLV_CONF}" ${ORIG_RESOLV_CONF} else echo "Current resolv.conf is not generated by Libreswan, so doing nothing" fi rm -f -- "${LIBRESWAN_RESOLV_CONF}" return 0 else # Here disconnect signal is sent to NetworkManager # whenever an already established connection is being terminated. unset libreswan_reason unset PLUTO_PEER_DOMAIN_INFO unset PLUTO_PEER_DNS_INFO unset PLUTO_PEER_BANNER unset PLUTO_MY_SOURCEIP unset PLUTO_PEER echo "Restoring resolv.conf is controlled by Network Manager" disconnectNM fi } disconnectNM() { # This will be called whenever a connection fails to establish # due to a state (either phase 1, xauth phase, or phase 2) fails. # This will send a singal to NetworkManager over dbus so that NM # can clear up coonnections. libreswan_reason=disconnect export libreswan_reason echo "sending disconnect signal to NetworkManager" /usr/libexec/nm-libreswan-service-helper return 0 } addsource() { st=0 # check if given sourceip is local and add as alias if not if ! ip -o route get ${PLUTO_MY_SOURCEIP%/*} | grep -q ^local; then it="ip addr add ${PLUTO_MY_SOURCEIP%/*}/32 dev ${PLUTO_INTERFACE%:*}" oops="$(eval ${it} 2>&1)" st=$? if [ -z "${oops}" -a ${st} -ne 0 ]; then oops="silent error, exit status ${st}" fi case "${oops}" in 'RTNETLINK answers: File exists'*) # should not happen, but ... ignore if the # address was already assigned on interface oops="" st=0 ;; esac if [ -n "${oops}" -o ${st} -ne 0 ]; then echo "${0}: addsource \"${it}\" failed (${oops})" >&2 fi fi return ${st} } # WARNING: changesource might not work as expected with mast changesource() { st=0 parms="${PLUTO_PEER_CLIENT}" parms2="dev ${PLUTO_INTERFACE}" parms3="src ${PLUTO_MY_SOURCEIP%/*}" if [ -n "${IPROUTETABLE}" ]; then parms3="${parms3} table ${IPROUTETABLE}" fi it="ip route replace ${parms} ${parms2} ${parms3}" case "${PLUTO_PEER_CLIENT}" in "0.0.0.0/0"|"::/0") # opportunistic encryption work around it= ;; esac oops="$(eval ${it} 2>&1)" st=$? if [ -z "${oops}" -a ${st} -ne 0 ]; then oops="silent error, exit status ${st}" fi if [ -n "${oops}" -o ${st} -ne 0 ]; then echo "${0}: changesource \"${it}\" failed (${oops})" >&2 fi return ${st} } # the purpose of this command is to add an entry to the NEW_IPSEC_CONN chain # in the iptables system. It grabs the right packets and does a --set-mark # on them. An advanced routing rule then directs the packets to the # appropriate device with the right mark. # # This is not be done for OE packets --- they are supposed to get a # new netfilter module instead. doipsecrule() { if [ -z "${PLUTO_SAREF_TRACKING}" -o "${PLUTO_SAREF_TRACKING}" = "no" ]; then echo "SAref tracking left to third party" elif [ -z "${PLUTO_CONN_ADDRFAMILY}" -o "${PLUTO_CONN_ADDRFAMILY}" = "ipv6" ]; then echo "SAref not activated for IPv6" else saref=${PLUTO_PEER_REF} nf_saref=$(printf "0x%x0000" $(( ${saref} | 0x8000 ))) srcnet=${PLUTO_MY_CLIENT_NET}/${PLUTO_MY_CLIENT_MASK} dstnet=${PLUTO_PEER_CLIENT_NET}/${PLUTO_PEER_CLIENT_MASK} #echo SAref: ${saref} '->' ${nf_saref} rulespec="--src ${srcnet} --dst ${dstnet} -m mark --mark 0/0x80000000 -j MARK --set-mark ${nf_saref}" if ${use_comment}; then # WARNING: you should only edit use_comment while you have no established conns rulespec="${rulespec} -m comment --comment '${PLUTO_CONNECTION}'" fi case ${1} in add) it4="iptables -t mangle -I NEW_IPSEC_CONN 1 ${rulespec}" it6="ip6tables -t mangle -I NEW_IPSEC_CONN 1 ${rulespec}" ;; delete) it4="iptables -t mangle -D NEW_IPSEC_CONN ${rulespec}" it6="ip6tables -t mangle -D NEW_IPSEC_CONN ${rulespec}" ;; esac oops="$(set +x; eval ${it4}; eval ${it6}; 2>&1)" st=$? if [ -z "${oops}" -a ${st} -ne 0 ]; then oops="silent error, exit status ${st}" fi if [ -n "${oops}" -o ${st} -ne 0 ]; then echo "${0}: doipsecrule \"${it4}\" or \"${it6}\" failed (${oops})" >&2 fi return ${st} fi } # the big choice case "${PLUTO_VERB}:${1}" in spdadd-client:*|spdadd-host:*) checkipsec; doipsecrule add;; spddel-client:*|spddel-host:*) checkipsec doipsecrule delete;; prepare-host:*|prepare-client:*) # set up a "%trap" equivalent (we don't know how do this yet!) ;; route-host:*|route-client:*) # connection to me or my client subnet being routed # setup of %TRAP equivalent ;; unroute-host:*|unroute-client:*) # connection to me or my client subnet being unrouted # remove %TRAP equivalent ;; up-host:*) # If you are doing a custom version, firewall commands go here. ;; down-host:*) # If you are doing a custom version, firewall commands go here. ;; up-client:) # If you are doing a custom version, firewall commands go here. updateresolvconf ;; down-client:) # If you are doing a custom version, firewall commands go here. restoreresolvconf ;; disconnectNM-host|disconnectNM-client) # sending disconnect signal to NM, as something went wrong. disconnectNM ;; # # IPv6 # prepare-host-v6:*|prepare-client-v6:*) ;; route-host-v6:*|route-client-v6:*) # connection to me or my client subnet being routed #uproute_v6 ;; unroute-host-v6:*|unroute-client-v6:*) # connection to me or my client subnet being unrouted #downroute_v6 ;; up-host-v6:*) # connection to me coming up # If you are doing a custom version, firewall commands go here. ;; down-host-v6:*) # connection to me going down # If you are doing a custom version, firewall commands go here. ;; up-client-v6:) # connection to my client subnet coming up # If you are doing a custom version, firewall commands go here. ;; down-client-v6:) # connection to my client subnet going down # If you are doing a custom version, firewall commands go here. ;; *) echo "${0}: unknown verb \"${PLUTO_VERB}\" or parameter \"${1}\"" >&2 exit 1 ;; esac