Skip to content

Commit

Permalink
Fixes T1298 use vti tunnel with ipsec and dhcp.
Browse files Browse the repository at this point in the history
* make dhcp interface work for vti interfaces

* clean up code, loger timeout use python api

* change vti tunnel ip on new dhcp lease

* only change ip on up and do not get non dhcp ip

* fix error in function, include up-host and down-host
  • Loading branch information
UnicronNL committed Mar 14, 2019
1 parent 9900fb6 commit bcdf0de
Show file tree
Hide file tree
Showing 6 changed files with 266 additions and 112 deletions.
4 changes: 2 additions & 2 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ etcudevdir = /etc/udev
initddir = /etc/init.d
logrotatedir = /etc/logrotate.d
curverdir = $(sysconfdir)/config-migrate/current
bin_sudo_usersdir = $(bindir)/sudo-users
bin_sudo_usersdir = /usr/libexec/vyos/system

sbin_SCRIPTS =

Expand All @@ -14,7 +14,7 @@ sbin_SCRIPTS += scripts/dmvpn-config.pl
sbin_SCRIPTS += scripts/vyatta-vpn-ppp-updown.pl
sbin_SCRIPTS += scripts/vyatta-vti-config.pl

bin_sudo_users_SCRIPTS = scripts/vyatta-ipsec-dhcp.pl
bin_sudo_users_SCRIPTS = scripts/vyatta-ipsec-dhcp.py
share_perl5_DATA = lib/Vyatta/VPN/Util.pm
share_perl5_DATA += lib/Vyatta/VPN/vtiIntf.pm

Expand Down
5 changes: 4 additions & 1 deletion scripts/vpn-config.pl
Original file line number Diff line number Diff line change
Expand Up @@ -1078,6 +1078,9 @@
vpn_die(["vpn","ipsec","site-to-site","peer",$peer,"vti","bind"],"$vpn_cfg_err No interface bind specified for peer \"$peer\" vti\n");
}
$genout .= "\tleftupdown=\"/usr/lib/ipsec/vti-up-down $tunName\"\n";
if (defined($dhcp_iface)){
$dhcp_if = $dhcp_if + 1;
}
}

#
Expand Down Expand Up @@ -1522,7 +1525,7 @@ sub dhcp_hook {
if ($dhcp_iface > 0){
$str =<<EOS;
#!/bin/sh
/opt/vyatta/bin/sudo-users/vyatta-ipsec-dhcp.pl --interface=\"\$interface\" --new_ip=\"\$new_ip_address\" --reason=\"\$reason\" --old_ip=\"\$old_ip_address\"
/usr/libexec/vyos/system/vyatta-ipsec-dhcp.py --interface=\"\$interface\" --new_ip=\"\$new_ip_address\" --reason=\"\$reason\" --old_ip=\"\$old_ip_address\"
EOS
}
my $hook = "/etc/dhcp/dhclient-exit-hooks.d/ipsecd";
Expand Down
5 changes: 3 additions & 2 deletions scripts/vti-up-down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
source /etc/default/vyatta
source /etc/default/locale
case "$PLUTO_VERB" in
route-client | up-client)
route-client | up-client | up-host)
/bin/ip route delete default table 220
/opt/vyatta/sbin/vyatta-vti-config.pl --updown --intf=$1 --action=up
;;
down-client)
down-client | down-host)
/opt/vyatta/sbin/vyatta-vti-config.pl --updown --intf=$1 --action=down
;;
*)
;;
esac
exit 0

104 changes: 0 additions & 104 deletions scripts/vyatta-ipsec-dhcp.pl

This file was deleted.

220 changes: 220 additions & 0 deletions scripts/vyatta-ipsec-dhcp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
#!/usr/bin/env python3
#
# Copyright (C) 2019 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# 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.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#

import os
import re
import sys
import subprocess
import argparse
import syslog
import time
import vici

import vyos.config

config_file = "/etc/ipsec.conf";
secrets_file = "/etc/ipsec.secrets";


def parse_cli_args():
parser = argparse.ArgumentParser()
parser.add_argument("--interface", type=str)
parser.add_argument("--new_ip", type=str)
parser.add_argument("--old_ip", type=str)
parser.add_argument("--reason", type=str)
args = parser.parse_args()
return args


def ipsec_conf_r():
header = ''
footer = ''
finheader = 0
connlist = list()
conndict = dict()
curconn = ''

with open(config_file) as f:
for line in f:
if re.search('^\s*$', line) and finheader:
continue
if re.search('\#conn.*', line):
curconn = ''
continue
if re.search('(peer-.*-tunnel.*)', line):
finheader = 1
connid = re.search(r'(peer-.*-tunnel.*)', line).group(1)
curconn = connid
if connid not in connlist:
conndict[connid] = dict()
conndict[connid]['_dhcp_iface'] = None
conndict[connid]['_lip'] = None
conndict[connid]['_lines'] = list()
elif re.search('dhcp-interface=(.*)', line) and curconn != '':
conndict[connid]['_dhcp_iface'] = re.search(r'dhcp-interface=(.*)', line).group(1)
elif re.search('left=(.*)', line) and curconn != '':
conndict[connid]['_lip'] = re.search(r'left=(.*)', line).group(1)
elif not finheader:
header = header + line
elif curconn != '':
conndict[connid]['_lines'].append(line)
elif curconn == '':
footer = footer + line;
connlist.append(conndict)
return connlist, header, footer


def ipsec_conf_w(connlist, header, footer, interface, new_ip):
try:
with open(config_file, 'w') as f:
f.write('{0}\n'.format(header))
for connid in connlist:
connname = next(iter(connid))
f.write('conn {0}\n'.format(connname))
if connid[connname]['_dhcp_iface']:
if connid[connname]['_dhcp_iface'] == interface:
if not new_ip:
new_ip = ''
connid[connname]['_lip'] = new_ip
f.write('\t#dhcp-interface={0}\n'.format(connid[connname]['_dhcp_iface']))
f.write('\tleft={0}\n'.format(connid[connname]['_lip']))
for line in connid[connname]['_lines']:
f.write('{0}'.format(line))
f.write('#conn {0}\n\n'.format(connname))
f.write('{0}\n'.format(footer))
except EnvironmentError as e:
sys.exit('Can\'t open {0}: {1}'.format(config_file, e))


def ipsec_sec_r():
lines = []

with open(secrets_file) as f:
lines = [line for line in f]
return lines


def ipsec_sec_w(lines, interface, new_ip):
try:
with open(secrets_file, 'w') as f:
for line in lines:
if re.search('(.*)\#dhcp-interface=(.*)\#', line) and \
re.search('(.*)\#dhcp-interface=(.*)\#', line).group(2) == interface:
secretline = re.search('(.*)\#dhcp-interface=(.*)\#', line).group(1)
if not new_ip:
new_ip = "#"
secline = re.search('(.*?) (.*?) : PSK (.*?) #dhcp', line)
line = '{0} {1} : PSK {2} #dhcp-interface={3}#\n'.format(new_ip,
secline.group(2), secline.group(3), interface)
f.write('{0}'.format(line))
except EnvironmentError as e:
sys.exit('Can\'t open {0}: {1}'.format(config_file, e))


def conn_list():
v = vici.Session()
config = vyos.config.Config()
config_conns = config.list_effective_nodes("vpn ipsec site-to-site peer")
connup = []


v = vici.Session()
for conn in v.list_sas():
for key in conn:
for c_conn in config_conns:
if c_conn in key:
if config.return_effective_value("vpn ipsec site-to-site peer {0} dhcp-interface".format(c_conn)):
connup.append(key)

return connup


def run(*popenargs, input=None, check=False, **kwargs):
if input is not None:
if 'stdin' in kwargs:
raise ValueError('stdin and input arguments may not both be used.')
kwargs['stdin'] = subprocess.PIPE

process = subprocess.Popen(*popenargs, **kwargs)
try:
stdout, stderr = process.communicate(input)
except:
process.kill()
process.wait()
raise
retcode = process.poll()
if check and retcode:
raise subprocess.CalledProcessError(
retcode, process.args, output=stdout, stderr=stderr)
return retcode, stdout, stderr


def term_conn(active_conn):
v = vici.Session()
for conn in active_conn:
try:
list(v.terminate({"ike": conn, "force": "true"}))
except:
pass


def reload_conn():
run(["/usr/sbin/ipsec", "rereadall"], stderr=subprocess.DEVNULL,
stdout=subprocess.DEVNULL)
run(["/usr/sbin/ipsec", "update"], stderr=subprocess.DEVNULL,
stdout=subprocess.DEVNULL)


def init_conn(active_conn, updated_conn):
v = vici.Session()
for conn in active_conn:
if conn not in updated_conn:
list(v.initiate({"child": conn, "timeout": "10000"}))


def main():
args = parse_cli_args()
syslog.openlog('ipsec-dhclient-hook')

syslog.syslog(syslog.LOG_NOTICE, 'Receive DHCP address updated to {0} from {1}'
', reason: {2}.'.format(args.new_ip, args.old_ip, args.reason))

if args.old_ip == args.new_ip and args.reason != 'BOUND' or args.reason == 'REBOOT' or args.reason == 'EXPIRE':
syslog.syslog(syslog.LOG_NOTICE, 'No ipsec update needed.')
sys.exit(0)

syslog.syslog(syslog.LOG_NOTICE, 'DHCP address updated to {0} from {1}: '
' Updating ipsec configuration, reason: {2}.'.format(args.new_ip, args.old_ip, args.reason))

connlist, header, footer = ipsec_conf_r()
ipsec_conf_w(connlist, header, footer, args.interface, args.new_ip)

lines = ipsec_sec_r()
ipsec_sec_w(lines, args.interface, args.new_ip)

if args.new_ip:
active_conn = conn_list()
term_conn(active_conn)
reload_conn()
time.sleep(5)
updated_conn = conn_list()
init_conn(active_conn, updated_conn)


if __name__ == '__main__':
main()
Loading

0 comments on commit bcdf0de

Please sign in to comment.