summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile.am4
-rwxr-xr-xscripts/vpn-config.pl5
-rwxr-xr-xscripts/vti-up-down5
-rwxr-xr-xscripts/vyatta-ipsec-dhcp.pl104
-rwxr-xr-xscripts/vyatta-ipsec-dhcp.py220
-rwxr-xr-xscripts/vyatta-vti-config.pl40
6 files changed, 266 insertions, 112 deletions
diff --git a/Makefile.am b/Makefile.am
index 1dc45b1..2da9b18 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 =
@@ -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
diff --git a/scripts/vpn-config.pl b/scripts/vpn-config.pl
index ace33e8..d68e419 100755
--- a/scripts/vpn-config.pl
+++ b/scripts/vpn-config.pl
@@ -1078,6 +1078,9 @@ if ($vcVPN->exists('ipsec')) {
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;
+ }
}
#
@@ -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";
diff --git a/scripts/vti-up-down b/scripts/vti-up-down
index 8d363da..08e31c0 100755
--- a/scripts/vti-up-down
+++ b/scripts/vti-up-down
@@ -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
+
diff --git a/scripts/vyatta-ipsec-dhcp.pl b/scripts/vyatta-ipsec-dhcp.pl
deleted file mode 100755
index 6b8782c..0000000
--- a/scripts/vyatta-ipsec-dhcp.pl
+++ /dev/null
@@ -1,104 +0,0 @@
-#!/usr/bin/perl
-use Getopt::Long;
-use strict;
-
-my $config_file = "/etc/ipsec.conf";
-my $secrets_file = "/etc/ipsec.secrets";
-
-sub logger {
- my $msg = pop(@_);
- my $FACILITY = "daemon";
- my $LEVEL = "notice";
- my $TAG = "ipsec-dhclient-hook";
- my $LOGCMD = "logger -t $TAG -p $FACILITY.$LEVEL";
- system("$LOGCMD $msg");
-}
-
-my ($iface, $config_iface, $nip, $oip, $reason);
-GetOptions("interface=s" => \$iface,
- "new_ip=s" => \$nip,
- "old_ip=s" => \$oip,
- "reason=s" => \$reason);
-
-# check if an update is needed
-exit(0) if (($oip eq $nip) && ($reason ne "BOUND"));
-logger("DHCP address updated to $nip from $oip: Updating ipsec configuration.");
-
-# open ipsec config
-open (my $FD, '<', $config_file);
-my $header = '';
-my $footer = '';
-my $finheader = 0;
-my %connhash = ();
-my $curconn = '';
-foreach my $line (<$FD>){
- next if (($line =~/^\s*$/) && $finheader);
- if ($line =~ /\#conn.*/){
- $curconn = '';
- next;
- }
- if ($line =~ /(peer-.*-tunnel.*)/){
- $finheader = 1;
- my $connid = $1;
- $curconn = $connid;
- if (not exists $connhash{$connid}){
- $connhash{$connid} = {
- _dhcp_iface => undef,
- _lip => undef,
- _lines => []
- };
- }
- } elsif (($line =~ /dhcp-interface=(.*)/) && ($curconn ne '') ){
- $connhash{$curconn}->{_dhcp_iface}=$1;
- } elsif (($line =~ /left=(.*)/) && ($curconn ne '') ){
- $connhash{$curconn}->{_lip}=$1;
- } elsif (!$finheader){
- $header .= $line;
- } elsif ($curconn ne ''){
- push (@{$connhash{"$curconn"}->{_lines}}, $line);
- } elsif ($curconn eq ''){
- $footer .= $line;
- }
-}
-close($FD);
-
-# output new ipsec.conf
-open my $output_config, '>', $config_file
- or die "Can't open $config_file: $!";
-
-print ${output_config} "$header\n";
-foreach my $connid ( keys (%connhash)){
- print ${output_config} "conn $connid\n";
- if (defined($connhash{$connid}->{_dhcp_iface})){
- if ($connhash{$connid}->{_dhcp_iface} eq $iface){
- $connhash{$connid}->{_lip} = $nip;
- }
- print ${output_config} "\t\#dhcp-interface=$connhash{$connid}->{_dhcp_iface}\n";
- }
- print ${output_config} "\tleft=$connhash{$connid}->{_lip}\n";
- foreach my $line (@{$connhash{$connid}->{_lines}}){
- print ${output_config} $line;
- }
- print ${output_config} "\#conn $connid\n\n";
-}
-print ${output_config} "$footer\n";
-close $output_config;
-
-# change ipsec.secrets
-open (my $FD, '<', $secrets_file);
-my @lines = <$FD>;
-close FD;
-open my $output_secrets, '>', $secrets_file
- or die "Can't open $secrets_file";
-foreach my $line (@lines){
- if (($line =~ /(.*)\#dhcp-interface=(.*)\#/) && ($2 eq $iface)){
- my $secretline = $1;
- $nip = "#" if ($nip eq '');
- $secretline =~ /(.*?) (.*?) : PSK (.*)/;
- $line = "$nip $2 : PSK $3\#dhcp-interface=$iface\#\n";
- }
- print ${output_secrets} $line;
-}
-close $output_secrets;
-system ("/usr/sbin/ipsec rereadall > /dev/null 2>&1");
-system ("/usr/sbin/ipsec update > /dev/null 2>&1");
diff --git a/scripts/vyatta-ipsec-dhcp.py b/scripts/vyatta-ipsec-dhcp.py
new file mode 100755
index 0000000..5aaa86e
--- /dev/null
+++ b/scripts/vyatta-ipsec-dhcp.py
@@ -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()
diff --git a/scripts/vyatta-vti-config.pl b/scripts/vyatta-vti-config.pl
index 81abf97..0886202 100755
--- a/scripts/vyatta-vti-config.pl
+++ b/scripts/vyatta-vti-config.pl
@@ -129,9 +129,12 @@ foreach my $peer (@peers) {
my $change = 0;
# Check local address is valid.
- if (!defined($lip)) {
- print STDERR "$vti_cfg_err local-address not defined.\n";
- exit -1;
+ my $dhcp_iface = $vcVPN->returnValue("ipsec site-to-site peer $peer dhcp-interface");
+ if (defined($lip) && defined($dhcp_iface)){
+ vti_die(["vpn","ipsec","site-to-site","peer",$peer],"$vti_cfg_err Only one of local-address or dhcp-interface may be defined");
+ }
+ if (defined($dhcp_iface)){
+ $lip = get_dhcp_addr($dhcp_iface, $peer);
}
if (!(validateType('ipv4', $lip, 'quiet') || validateType('ipv6', $lip, 'quiet')) || ($lip eq '0.0.0.0')) {
@@ -232,9 +235,25 @@ sub vti_handle_updown {
$vcIntf->setLevel('interfaces');
my $disabled = $vcIntf->existsOrig("vti $intfName disabled");
if (!defined($disabled) || !$disabled) {
+ my $vcVPN = new Vyatta::Config();
+ $vcVPN->setLevel('vpn ipsec site-to-site');
+ my @peers = $vcVPN->listOrigNodes('peer');
my $vtiInterface = new Vyatta::Interface($intfName);
my $state = $vtiInterface->up();
if (!($state && ($action eq "up"))) {
+ if ($action eq "up") {
+ foreach my $peer (@peers) {
+ if (!$vcVPN->existsOrig("peer $peer vti bind $intfName")) {
+ next;
+ }
+
+ my $dhcp_iface = $vcVPN->returnOrigValue("peer $peer dhcp-interface");
+ if (defined($dhcp_iface)) {
+ my $lip = get_dhcp_addr($dhcp_iface, $peer);
+ system("sudo /sbin/ip tunnel change $intfName local $lip\n");
+ }
+ }
+ }
system("sudo /sbin/ip link set $intfName $action\n");
}
}
@@ -315,3 +334,18 @@ sub checkUnrefIntfVti {
}
}
}
+
+sub get_dhcp_addr {
+ my ($dhcp_iface, $peer) = @_;
+ vti_die(["vpn","ipsec","site-to-site","peer",$peer,"dhcp-interface"],"$vti_cfg_err The specified interface is not configured for dhcp.")
+ if (!(Vyatta::Misc::is_dhcp_enabled($dhcp_iface,0)));
+ my @dhcp_addr = Vyatta::Misc::getIP($dhcp_iface,4);
+ my $addr = pop(@dhcp_addr);
+ if (!defined($addr)){
+ $addr = '';
+ return $addr;
+ }
+ @dhcp_addr = split(/\//, $addr);
+ $addr = $dhcp_addr[0];
+ return $addr;
+}