diff options
Diffstat (limited to 'scripts/vpn-config.pl')
-rwxr-xr-x | scripts/vpn-config.pl | 2379 |
1 files changed, 1271 insertions, 1108 deletions
diff --git a/scripts/vpn-config.pl b/scripts/vpn-config.pl index 3410cab..2ae7edf 100755 --- a/scripts/vpn-config.pl +++ b/scripts/vpn-config.pl @@ -1,37 +1,37 @@ #!/usr/bin/perl -w # # Module: vpn-config.pl -# +# # **** License **** # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 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. -# +# # This code was originally developed by Vyatta, Inc. # Portions created by Vyatta are Copyright (C) 2006, 2007 Vyatta, Inc. # All Rights Reserved. -# +# # Authors: Justin Fletcher, Marat Nepomnyashy # Date: 2007 # Description: Start Openswan VPN based on verified configuration # # **** End License **** -# +# use strict; use lib "/opt/vyatta/share/perl5"; -use constant IKELIFETIME_DEFAULT => 28800; # 8 hours -use constant ESPLIFETIME_DEFAULT => 3600; # 1 hour -use constant REKEYMARGIN_DEFAULT => 540; # 9 minutes -use constant REKEYFUZZ_DEFAULT => 100; -use constant INVALID_LOCAL_IP => 254; -use constant VPN_MAX_PROPOSALS => 10; +use constant IKELIFETIME_DEFAULT => 28800; # 8 hours +use constant ESPLIFETIME_DEFAULT => 3600; # 1 hour +use constant REKEYMARGIN_DEFAULT => 540; # 9 minutes +use constant REKEYFUZZ_DEFAULT => 100; +use constant INVALID_LOCAL_IP => 254; +use constant VPN_MAX_PROPOSALS => 10; use Vyatta::VPN::Util; use Getopt::Long; @@ -42,15 +42,16 @@ my $newconfig_dir; my $config_file; my $secrets_file; my $init_script; -GetOptions("changes_dir=s" => \$changes_dir, - "newconfig_dir=s" => \$newconfig_dir, - "config_file=s" => \$config_file, - "secrets_file=s" => \$secrets_file, - "init_script=s" => \$init_script +GetOptions( + "changes_dir=s" => \$changes_dir, + "newconfig_dir=s" => \$newconfig_dir, + "config_file=s" => \$config_file, + "secrets_file=s" => \$secrets_file, + "init_script=s" => \$init_script ); my $clustering_ip = 0; -my $error = 0; +my $error = 0; my $genout; my $genout_secrets; @@ -58,1201 +59,1363 @@ my $genout_secrets; # Set it to 0 us using NETKEY. my $using_klips = 0; -$genout .= "# generated by $0\n\n"; +$genout .= "# generated by $0\n\n"; $genout_secrets .= "# generated by $0\n\n"; - # # Prepare Vyatta::Config object -# +# use Vyatta::Config; -my $vc = new Vyatta::Config(); +my $vc = new Vyatta::Config(); my $vcVPN = new Vyatta::Config(); -if (defined($changes_dir) && $changes_dir ne '') { - $vc->{_changes_only_dir_base} = $changes_dir; - $vcVPN->{_changes_only_dir_base} = $changes_dir; +if ( defined($changes_dir) && $changes_dir ne '' ) { + $vc->{_changes_only_dir_base} = $changes_dir; + $vcVPN->{_changes_only_dir_base} = $changes_dir; } -if (defined($newconfig_dir) && $newconfig_dir ne '') { - $vc->{_new_config_dir_base} = $newconfig_dir; - $vcVPN->{_new_config_dir_base} = $newconfig_dir; +if ( defined($newconfig_dir) && $newconfig_dir ne '' ) { + $vc->{_new_config_dir_base} = $newconfig_dir; + $vcVPN->{_new_config_dir_base} = $newconfig_dir; } -$genout .= "# using 'changes only' directory: $vcVPN->{_changes_only_dir_base}\n"; -$genout .= "# using 'new config' directory: $vcVPN->{_new_config_dir_base}\n\n"; - -$genout_secrets .= "# using 'changes only' directory: $vcVPN->{_changes_only_dir_base}\n"; -$genout_secrets .= "# using 'new config' directory: $vcVPN->{_new_config_dir_base}\n\n"; - +$genout .= + "# using 'changes only' directory: $vcVPN->{_changes_only_dir_base}\n"; +$genout .= + "# using 'new config' directory: $vcVPN->{_new_config_dir_base}\n\n"; +$genout_secrets .= + "# using 'changes only' directory: $vcVPN->{_changes_only_dir_base}\n"; +$genout_secrets .= + "# using 'new config' directory: $vcVPN->{_new_config_dir_base}\n\n"; $vcVPN->setLevel('vpn'); -if ($vcVPN->exists('ipsec')) { - - # - # Check that ESP groups have been specified - # - my @esp_groups = $vcVPN->listNodes('ipsec esp-group'); - if (@esp_groups == 0) { - #$error = 1; - #print STDERR "VPN configuration error. No ESP groups configured. At least one ESP group required.\n"; - # XXX for now this will be checked below for site-to-site peer - } else { - foreach my $esp_group (@esp_groups) { - my @esp_group_proposals = $vcVPN->listNodes("ipsec esp-group $esp_group proposal"); - if (@esp_group_proposals == 0) { - $error = 1; - print STDERR "VPN configuration error. No proposals configured for ESP group \"$esp_group\". At least one proposal required.\n"; - } elsif (@esp_group_proposals > VPN_MAX_PROPOSALS) { - $error = 1; - print STDERR 'VPN configuration error. A total of ' . @esp_group_proposals . " proposals have been configured for ESP group \"$esp_group\". The maximum proposals allowed for an ESP group is " . VPN_MAX_PROPOSALS . "\n"; - } else { - foreach my $esp_group_proposal (@esp_group_proposals) { - my $esp_group_proposal_encryption = $vcVPN->returnValue("ipsec esp-group $esp_group proposal $esp_group_proposal encryption"); - if (!defined($esp_group_proposal_encryption) || $esp_group_proposal_encryption eq "") { - $error = 1; - print STDERR "VPN configuration error. No encryption specified for ESP group \"$esp_group\" proposal $esp_group_proposal.\n"; - } - my $esp_group_proposal_hash = $vcVPN->returnValue("ipsec esp-group $esp_group proposal $esp_group_proposal hash"); - if (!defined($esp_group_proposal_hash) || $esp_group_proposal_hash eq "") { - $error = 1; - print STDERR "VPN configuration error. No hash specified for ESP group \"$esp_group\" proposal $esp_group_proposal.\n"; - } - } - } - } +if ( $vcVPN->exists('ipsec') ) { + + # + # Check that ESP groups have been specified + # + my @esp_groups = $vcVPN->listNodes('ipsec esp-group'); + if ( @esp_groups == 0 ) { + +#$error = 1; +#print STDERR "VPN configuration error. No ESP groups configured. At least one ESP group required.\n"; +# XXX for now this will be checked below for site-to-site peer + } else { + foreach my $esp_group (@esp_groups) { + my @esp_group_proposals = + $vcVPN->listNodes("ipsec esp-group $esp_group proposal"); + if ( @esp_group_proposals == 0 ) { + $error = 1; + print STDERR +"VPN configuration error. No proposals configured for ESP group \"$esp_group\". At least one proposal required.\n"; + } elsif ( @esp_group_proposals > VPN_MAX_PROPOSALS ) { + $error = 1; + print STDERR 'VPN configuration error. A total of ' + . @esp_group_proposals + . " proposals have been configured for ESP group \"$esp_group\". The maximum proposals allowed for an ESP group is " + . VPN_MAX_PROPOSALS . "\n"; + } else { + foreach my $esp_group_proposal (@esp_group_proposals) { + my $esp_group_proposal_encryption = $vcVPN->returnValue( + "ipsec esp-group $esp_group proposal $esp_group_proposal encryption" + ); + if ( !defined($esp_group_proposal_encryption) + || $esp_group_proposal_encryption eq "" ) + { + $error = 1; + print STDERR +"VPN configuration error. No encryption specified for ESP group \"$esp_group\" proposal $esp_group_proposal.\n"; + } + my $esp_group_proposal_hash = $vcVPN->returnValue( + "ipsec esp-group $esp_group proposal $esp_group_proposal hash"); + if ( !defined($esp_group_proposal_hash) + || $esp_group_proposal_hash eq "" ) + { + $error = 1; + print STDERR +"VPN configuration error. No hash specified for ESP group \"$esp_group\" proposal $esp_group_proposal.\n"; + } + } + } } - - + } + + # + # Check that IKE groups have been specified + # + my @ike_groups = $vcVPN->listNodes('ipsec ike-group'); + if ( @ike_groups == 0 ) { + +#$error = 1; +#print STDERR "VPN configuration error. No IKE groups configured. At least one IKE group required.\n"; +# XXX for now this will be checked below for site-to-site peer + } else { + foreach my $ike_group (@ike_groups) { + my @ike_group_proposals = + $vcVPN->listNodes("ipsec ike-group $ike_group proposal"); + if ( @ike_group_proposals == 0 ) { + $error = 1; + print STDERR +"VPN configuration error. No proposals configured for IKE group \"$ike_group\". At least one proposal required.\n"; + } elsif ( @ike_group_proposals > VPN_MAX_PROPOSALS ) { + $error = 1; + print STDERR 'VPN configuration error. A total of ' + . @ike_group_proposals + . " proposals have been configured for IKE group \"$ike_group\". The maximum proposals allowed for an IKE group is " + . VPN_MAX_PROPOSALS . "\n"; + } else { + foreach my $ike_group_proposal (@ike_group_proposals) { + my $ike_group_proposal_encryption = $vcVPN->returnValue( + "ipsec ike-group $ike_group proposal $ike_group_proposal encryption" + ); + if ( !defined($ike_group_proposal_encryption) + || $ike_group_proposal_encryption eq "" ) + { + $error = 1; + print STDERR +"VPN configuration error. No encryption specified for IKE group \"$ike_group\" proposal $ike_group_proposal.\n"; + } + my $ike_group_proposal_hash = $vcVPN->returnValue( + "ipsec ike-group $ike_group proposal $ike_group_proposal hash"); + if ( !defined($ike_group_proposal_hash) + || $ike_group_proposal_hash eq "" ) + { + $error = 1; + print STDERR +"VPN configuration error. No hash specified for IKE group \"$ike_group\" proposal $ike_group_proposal.\n"; + } + } + } + } + } + + # + # Check the local key file + # Note: $local_key_file will be used later when reading the keys + # + my $running_local_key_file = rsa_get_local_key_file(); + my $local_key_file = $vcVPN->returnValue('rsa-keys local-key file'); + if ( !defined($local_key_file) ) { + $local_key_file = LOCAL_KEY_FILE_DEFAULT; + } + if ( $local_key_file ne $running_local_key_file ) { + + # Sanity check the usr specified local_key_file # - # Check that IKE groups have been specified + # 1). Must start with "/" + # 2). Only allow alpha-numeric, ".", "-", "_", or "/". + # 3). Don't allow "//" + # 4). Verify that it's not a directory # - my @ike_groups = $vcVPN->listNodes('ipsec ike-group'); - if (@ike_groups == 0) { - #$error = 1; - #print STDERR "VPN configuration error. No IKE groups configured. At least one IKE group required.\n"; - # XXX for now this will be checked below for site-to-site peer - } else { - foreach my $ike_group (@ike_groups) { - my @ike_group_proposals = $vcVPN->listNodes("ipsec ike-group $ike_group proposal"); - if (@ike_group_proposals == 0) { - $error = 1; - print STDERR "VPN configuration error. No proposals configured for IKE group \"$ike_group\". At least one proposal required.\n"; - } elsif (@ike_group_proposals > VPN_MAX_PROPOSALS) { - $error = 1; - print STDERR 'VPN configuration error. A total of ' . @ike_group_proposals . " proposals have been configured for IKE group \"$ike_group\". The maximum proposals allowed for an IKE group is " . VPN_MAX_PROPOSALS . "\n"; - } else { - foreach my $ike_group_proposal (@ike_group_proposals) { - my $ike_group_proposal_encryption = $vcVPN->returnValue("ipsec ike-group $ike_group proposal $ike_group_proposal encryption"); - if (!defined($ike_group_proposal_encryption) || $ike_group_proposal_encryption eq "") { - $error = 1; - print STDERR "VPN configuration error. No encryption specified for IKE group \"$ike_group\" proposal $ike_group_proposal.\n"; - } - my $ike_group_proposal_hash = $vcVPN->returnValue("ipsec ike-group $ike_group proposal $ike_group_proposal hash"); - if (!defined($ike_group_proposal_hash) || $ike_group_proposal_hash eq "") { - $error = 1; - print STDERR "VPN configuration error. No hash specified for IKE group \"$ike_group\" proposal $ike_group_proposal.\n"; - } - } - } - } + if ( $local_key_file !~ /^\// ) { + $error = 1; + print STDERR +"VPN configuration error. Invalid local RSA key file path \"$local_key_file\". Does not start with a '/'.\n"; } - - # - # Check the local key file - # Note: $local_key_file will be used later when reading the keys - # - my $running_local_key_file = rsa_get_local_key_file(); - my $local_key_file = $vcVPN->returnValue('rsa-keys local-key file'); - if (!defined($local_key_file)) { - $local_key_file = LOCAL_KEY_FILE_DEFAULT; + if ( $local_key_file =~ /[^a-zA-Z0-9\.\-\_\/]/g ) { + $error = 1; + print STDERR +"VPN configuration error. Invalid local RSA key file path \"$local_key_file\". Contains a character that is not alpha-numeric and not '.', '-', '_', '/'.\n"; } - if ($local_key_file ne $running_local_key_file) { - - # Sanity check the usr specified local_key_file - # - # 1). Must start with "/" - # 2). Only allow alpha-numeric, ".", "-", "_", or "/". - # 3). Don't allow "//" - # 4). Verify that it's not a directory - # - if ($local_key_file !~ /^\//) { - $error = 1; - print STDERR "VPN configuration error. Invalid local RSA key file path \"$local_key_file\". Does not start with a '/'.\n"; - } - if ($local_key_file =~ /[^a-zA-Z0-9\.\-\_\/]/g) { - $error = 1; - print STDERR "VPN configuration error. Invalid local RSA key file path \"$local_key_file\". Contains a character that is not alpha-numeric and not '.', '-', '_', '/'.\n"; - } - if ($local_key_file =~ /\/\//g) { - $error = 1; - print STDERR "VPN configuration error. Invalid local RSA key file path \"$local_key_file\". Contains string \"//\".\n"; - } - if (-d $local_key_file) { - $error = 1; - print STDERR "VPN configuration error. Invalid local RSA key file path \"$local_key_file\". Path is a directory rather than a file.\n"; - } - - if ($error == 0) { - if (-r $running_local_key_file && !(-e $local_key_file)) { - vpn_debug "cp $running_local_key_file $local_key_file"; - my ($dirpath) = ($local_key_file =~ m#^(.*/)?.*#s); - my $rc = system("mkdir -p $dirpath"); - if ($rc != 0) { - $error = 1; - print STDERR "VPN configuration error. Could not copy previous local RSA key file \"$running_local_key_file\" to new local RSA key file \"$local_key_file\". Could not mkdir [$dirpath] $!\n"; - } else { - $rc = system("cp $running_local_key_file $local_key_file"); - if ($rc != 0) { - $error = 1; - print STDERR "VPN configuration error. Could not copy previous local RSA key file \"$running_local_key_file\" to new local RSA key file \"$local_key_file\". $!\n"; - } - } - } - } + if ( $local_key_file =~ /\/\//g ) { + $error = 1; + print STDERR +"VPN configuration error. Invalid local RSA key file path \"$local_key_file\". Contains string \"//\".\n"; } - - # - # Version 2 - # - $genout .= "version 2.0\n"; - $genout .= "\n"; - $genout .= "config setup\n"; - - # - # Interfaces - # - my @interfaces = $vcVPN->returnValues('ipsec ipsec-interfaces interface'); - if (@interfaces == 0) { - $error = 1; - print STDERR "VPN configuration error. No IPSEC interfaces specified.\n"; - } else { - # We need to generate an "interfaces=..." entry in the setup section - # only if the underlying IPsec kernel code we are using is KLIPS. - # If we are using NETKEY, the "interfaces=..." entry is essentially - # not used, though we do need to include the line and the keyword - # "%none" to keep the IPsec setup code from defaulting the entry. - if ($using_klips) { - $genout .= "\tinterfaces=\""; - my $counter = 0; - foreach my $interface (@interfaces) { - if (!(-d "/sys/class/net/$interface")) { - next; - } - if ($counter > 0) { - $genout .= ' '; - } - $genout .= "ipsec$counter=$interface"; - ++$counter; - } - if (hasLocalWildcard($vcVPN, 0)) { - if ($counter > 0) { - $genout .= ' '; - } - $genout .= '%defaultroute'; - } - $genout .= "\"\n"; - } else { - my $counter = 0; - $genout .= "\t"; - if (hasLocalWildcard($vcVPN, 0)) { - $genout .= 'interfaces="'; - foreach my $interface (@interfaces) { - next if ! -d "/sys/class/net/$interface"; - next if scalar(getIP($interface)) < 1; - $genout .= "ipsec$counter=$interface "; - ++$counter; } - $genout .= '%defaultroute"'; - } else { - $genout .= 'interfaces="%none"'; - } - $genout .= "\n"; - } - } - - # - # NAT traversal - # - my $nat_traversal = $vcVPN->returnValue('ipsec nat-traversal'); - if (defined($nat_traversal)) { - if ($nat_traversal eq 'enable') { - $genout .= "\tnat_traversal=yes\n"; - } elsif ($nat_traversal eq 'disable') { - $genout .= "\tnat_traversal=no\n"; - } elsif ($nat_traversal ne '') { - $error = 1; - print STDERR "VPN configuration error. Invalid value \"$nat_traversal\" specified for 'nat-traversal'. Only \"enable\" or \"disable\" accepted.\n"; - } + if ( -d $local_key_file ) { + $error = 1; + print STDERR +"VPN configuration error. Invalid local RSA key file path \"$local_key_file\". Path is a directory rather than a file.\n"; } - # - # NAT networks - # - my @nat_networks = $vcVPN->listNodes('ipsec nat-networks allowed-network'); - if (@nat_networks > 0) { - my $first_nat_net = 1; - foreach my $nat_network (@nat_networks) { - if ($first_nat_net) { - $genout .= "\tvirtual_private=\"\%v4:$nat_network"; - $first_nat_net = 0; - } else { - $genout .= ",\%v4:$nat_network"; - } - - my @nat_network_excludes = $vcVPN->returnValues("ipsec nat-networks allowed-network $nat_network exclude"); - foreach my $nat_network_exclude (@nat_network_excludes) { - $genout .= ",\%v4:\!$nat_network_exclude"; - } - } - $genout .= "\"\n"; + if ( $error == 0 ) { + if ( -r $running_local_key_file && !( -e $local_key_file ) ) { + vpn_debug "cp $running_local_key_file $local_key_file"; + my ($dirpath) = ( $local_key_file =~ m#^(.*/)?.*#s ); + my $rc = system("mkdir -p $dirpath"); + if ( $rc != 0 ) { + $error = 1; + print STDERR +"VPN configuration error. Could not copy previous local RSA key file \"$running_local_key_file\" to new local RSA key file \"$local_key_file\". Could not mkdir [$dirpath] $!\n"; + } else { + $rc = system("cp $running_local_key_file $local_key_file"); + if ( $rc != 0 ) { + $error = 1; + print STDERR +"VPN configuration error. Could not copy previous local RSA key file \"$running_local_key_file\" to new local RSA key file \"$local_key_file\". $!\n"; + } + } + } } - - # - # copy-tos - # - my $copy_tos = $vcVPN->returnValue('ipsec copy-tos'); - if (defined($copy_tos) && $copy_tos eq 'enable') { - $genout .= "\thidetos=no\n"; + } + + # + # Version 2 + # + $genout .= "version 2.0\n"; + $genout .= "\n"; + $genout .= "config setup\n"; + + # + # Interfaces + # + my @interfaces = $vcVPN->returnValues('ipsec ipsec-interfaces interface'); + if ( @interfaces == 0 ) { + $error = 1; + print STDERR "VPN configuration error. No IPSEC interfaces specified.\n"; + } else { + + # We need to generate an "interfaces=..." entry in the setup section + # only if the underlying IPsec kernel code we are using is KLIPS. + # If we are using NETKEY, the "interfaces=..." entry is essentially + # not used, though we do need to include the line and the keyword + # "%none" to keep the IPsec setup code from defaulting the entry. + if ($using_klips) { + $genout .= "\tinterfaces=\""; + my $counter = 0; + foreach my $interface (@interfaces) { + if ( !( -d "/sys/class/net/$interface" ) ) { + next; + } + if ( $counter > 0 ) { + $genout .= ' '; + } + $genout .= "ipsec$counter=$interface"; + ++$counter; + } + if ( hasLocalWildcard( $vcVPN, 0 ) ) { + if ( $counter > 0 ) { + $genout .= ' '; + } + $genout .= '%defaultroute'; + } + $genout .= "\"\n"; } else { - $genout .= "\thidetos=yes\n"; + my $counter = 0; + $genout .= "\t"; + if ( hasLocalWildcard( $vcVPN, 0 ) ) { + $genout .= 'interfaces="'; + foreach my $interface (@interfaces) { + next if !-d "/sys/class/net/$interface"; + next if scalar( getIP($interface) ) < 1; + $genout .= "ipsec$counter=$interface "; + ++$counter; + } + $genout .= '%defaultroute"'; + } else { + $genout .= 'interfaces="%none"'; + } + $genout .= "\n"; } - - # - # Logging - # - my $facility = $vcVPN->returnValue('ipsec logging facility'); - my $level = $vcVPN->returnValue('ipsec logging level'); - if ((defined($facility) && $facility ne '') && (!defined($level) || $level eq '')) { - $error = 1; - print STDERR "VPN configuration error. VPN logging facility has been specified without the VPN logging level. One may not be specified without the other.\n"; - } elsif ((!defined($facility) || $facility eq '') && (defined($level) && $level ne '')) { - $error = 1; - print STDERR "VPN configuration error. VPN logging level has been specified without the VPN logging facility. One may not be specified without the other.\n"; + } + + # + # NAT traversal + # + my $nat_traversal = $vcVPN->returnValue('ipsec nat-traversal'); + if ( defined($nat_traversal) ) { + if ( $nat_traversal eq 'enable' ) { + $genout .= "\tnat_traversal=yes\n"; + } elsif ( $nat_traversal eq 'disable' ) { + $genout .= "\tnat_traversal=no\n"; + } elsif ( $nat_traversal ne '' ) { + $error = 1; + print STDERR +"VPN configuration error. Invalid value \"$nat_traversal\" specified for 'nat-traversal'. Only \"enable\" or \"disable\" accepted.\n"; } - - if (defined($level) and ($level eq "err")) { - $level = "error"; # This allows the cli to be consistent with syslog. + } + + # + # NAT networks + # + my @nat_networks = $vcVPN->listNodes('ipsec nat-networks allowed-network'); + if ( @nat_networks > 0 ) { + my $first_nat_net = 1; + foreach my $nat_network (@nat_networks) { + if ($first_nat_net) { + $genout .= "\tvirtual_private=\"\%v4:$nat_network"; + $first_nat_net = 0; + } else { + $genout .= ",\%v4:$nat_network"; + } + + my @nat_network_excludes = $vcVPN->returnValues( + "ipsec nat-networks allowed-network $nat_network exclude"); + foreach my $nat_network_exclude (@nat_network_excludes) { + $genout .= ",\%v4:\!$nat_network_exclude"; + } } - if (defined($facility) and defined($level)) { - $genout .= "\tsyslog=$facility.$level\n"; + $genout .= "\"\n"; + } + + # + # copy-tos + # + my $copy_tos = $vcVPN->returnValue('ipsec copy-tos'); + if ( defined($copy_tos) && $copy_tos eq 'enable' ) { + $genout .= "\thidetos=no\n"; + } else { + $genout .= "\thidetos=yes\n"; + } + + # + # Logging + # + my $facility = $vcVPN->returnValue('ipsec logging facility'); + my $level = $vcVPN->returnValue('ipsec logging level'); + if ( ( defined($facility) && $facility ne '' ) + && ( !defined($level) || $level eq '' ) ) + { + $error = 1; + print STDERR +"VPN configuration error. VPN logging facility has been specified without the VPN logging level. One may not be specified without the other.\n"; + } elsif ( ( !defined($facility) || $facility eq '' ) + && ( defined($level) && $level ne '' ) ) + { + $error = 1; + print STDERR +"VPN configuration error. VPN logging level has been specified without the VPN logging facility. One may not be specified without the other.\n"; + } + + if ( defined($level) and ( $level eq "err" ) ) { + $level = "error"; # This allows the cli to be consistent with syslog. + } + if ( defined($facility) and defined($level) ) { + $genout .= "\tsyslog=$facility.$level\n"; + } + + my @logmodes = $vcVPN->returnValues('ipsec logging log-modes'); + if ( @logmodes > 0 ) { + my $debugmode = ''; + foreach my $mode (@logmodes) { + if ( $mode eq "all" ) { + $debugmode = "all"; + last; + } + if ( $debugmode eq '' ) { + $debugmode = "$mode"; + } else { + $debugmode .= " $mode"; + } } - - my @logmodes = $vcVPN->returnValues('ipsec logging log-modes'); - if (@logmodes > 0) { - my $debugmode = ''; - foreach my $mode (@logmodes) { - if ($mode eq "all") { - $debugmode = "all"; - last; - } - if ($debugmode eq '') { - $debugmode = "$mode"; - } else { - $debugmode .= " $mode"; - } - } - $genout .= "\tplutodebug=\"$debugmode\"\n"; - } - - $genout .= "\tnhelpers=5\n"; - $genout .= "\tplutowait=yes\n"; - - # Set plutoopts: - # Disable uniqreqids? - # - if ($vcVPN->exists('ipsec disable-uniqreqids')) { - $genout .= "\tplutoopts=--disable-uniqreqids\n"; + $genout .= "\tplutodebug=\"$debugmode\"\n"; + } + + $genout .= "\tnhelpers=5\n"; + $genout .= "\tplutowait=yes\n"; + + # Set plutoopts: + # Disable uniqreqids? + # + if ( $vcVPN->exists('ipsec disable-uniqreqids') ) { + $genout .= "\tplutoopts=--disable-uniqreqids\n"; + } + + # + # Disable implicit connections + # + foreach my $conn ( + qw/clear clear-or-private private-or-clear private block packetdefault/) + { + $genout .= "\n"; + $genout .= "conn $conn\n"; + $genout .= "\tauto=ignore\n"; + } + + # + # Connection configurations + # + my $wildcard_psk = undef; + my @peers = $vcVPN->listNodes('ipsec site-to-site peer'); + if ( @peers == 0 && !( $vcVPN->exists('pptp') || $vcVPN->exists('l2tp') ) ) { + print +"VPN Warning: IPSec configured but no site-to-site peers or l2tp/pptp remote-users configured\n"; + } + foreach my $peer (@peers) { + my $peer_ike_group = + $vcVPN->returnValue("ipsec site-to-site peer $peer ike-group"); + if ( !defined($peer_ike_group) || $peer_ike_group eq '' ) { + $error = 1; + print STDERR +"VPN configuration error. No IKE group specified for peer \"$peer\".\n"; + } elsif ( !$vcVPN->exists("ipsec ike-group $peer_ike_group") ) { + $error = 1; + print STDERR +"VPN configuration error. The IKE group \"$peer_ike_group\" specified for peer \"$peer\" has not been configured.\n"; } - # - # Disable implicit connections - # - foreach my $conn (qw/clear clear-or-private private-or-clear private block packetdefault/) { - $genout .= "\n"; - $genout .= "conn $conn\n"; - $genout .= "\tauto=ignore\n"; + my $lip = $vcVPN->returnValue("ipsec site-to-site peer $peer local-ip"); + my $authid = + $vcVPN->returnValue("ipsec site-to-site peer $peer authentication id"); + my $authremoteid = $vcVPN->returnValue( + "ipsec site-to-site peer $peer authentication remote-id"); + if ( !defined($lip) || $lip eq "" ) { + $error = 1; + print STDERR + "VPN configuration error. No local-ip specified for peer \"$peer\"\n"; + } elsif ( $lip ne '0.0.0.0' ) { + + # not '0.0.0.0' special case. + # check interface addresses. + if ( !Vyatta::Misc::isIPinInterfaces( $vc, $lip, @interfaces ) ) { + vpn_log( +"The local-ip address $lip of peer \"$peer\" has not been configured in any of the ipsec-interfaces.\n" + ); + if ( Vyatta::Misc::isClusterIP( $vc, $lip ) ) { + + # Verified that dealing with a cluster IP. + $clustering_ip = 1; + } else { + print + "Warning: Local IPv4 address $lip specified for peer \"$peer\"\n"; + print + "is not configured on any of the ipsec-interfaces and is not the\n"; + print "clustering address. IPsec must be re-started after address\n"; + print "has been configured.\n"; + print "\n"; + } + } } # - # Connection configurations + # Name connection by peer and tunnel # - my $wildcard_psk = undef; - my @peers = $vcVPN->listNodes('ipsec site-to-site peer'); - if (@peers == 0 && !($vcVPN->exists('pptp') || $vcVPN->exists('l2tp'))) { - print "VPN Warning: IPSec configured but no site-to-site peers or l2tp/pptp remote-users configured\n"; + my @tunnels = $vcVPN->listNodes("ipsec site-to-site peer $peer tunnel"); + if ( @tunnels == 0 ) { + $error = 1; + print STDERR +"VPN configuration error. No tunnels configured for peer \"$peer\". At least one tunnel required per peer.\n"; } - foreach my $peer (@peers) { - my $peer_ike_group = $vcVPN->returnValue("ipsec site-to-site peer $peer ike-group"); - if (!defined($peer_ike_group) || $peer_ike_group eq '') { - $error = 1; - print STDERR "VPN configuration error. No IKE group specified for peer \"$peer\".\n"; - } elsif (!$vcVPN->exists("ipsec ike-group $peer_ike_group")) { - $error = 1; - print STDERR "VPN configuration error. The IKE group \"$peer_ike_group\" specified for peer \"$peer\" has not been configured.\n"; - } - - my $lip = $vcVPN->returnValue("ipsec site-to-site peer $peer local-ip"); - my $authid = $vcVPN->returnValue( - "ipsec site-to-site peer $peer authentication id"); - my $authremoteid = $vcVPN->returnValue( - "ipsec site-to-site peer $peer authentication remote-id"); - if (!defined($lip) || $lip eq "") { - $error = 1; - print STDERR "VPN configuration error. No local-ip specified for peer \"$peer\"\n"; - } elsif ($lip ne '0.0.0.0') { - # not '0.0.0.0' special case. - # check interface addresses. - if (!Vyatta::Misc::isIPinInterfaces($vc, $lip, @interfaces)) { - vpn_log("The local-ip address $lip of peer \"$peer\" has not been configured in any of the ipsec-interfaces.\n"); - if (Vyatta::Misc::isClusterIP($vc, $lip)) { - # Verified that dealing with a cluster IP. - $clustering_ip = 1; - } else { - print "Warning: Local IPv4 address $lip specified for peer \"$peer\"\n"; - print "is not configured on any of the ipsec-interfaces and is not the\n"; - print "clustering address. IPsec must be re-started after address\n"; - print "has been configured.\n"; - print "\n"; - } - } - } - - # - # Name connection by peer and tunnel - # - my @tunnels = $vcVPN->listNodes("ipsec site-to-site peer $peer tunnel"); - if (@tunnels == 0) { - $error = 1; - print STDERR "VPN configuration error. No tunnels configured for peer \"$peer\". At least one tunnel required per peer.\n"; - } - foreach my $tunnel (@tunnels) { - - # - # Add support for tunnel disable. - # - if ($vcVPN->exists("ipsec site-to-site peer $peer tunnel $tunnel disable")) { - next; - } - - - my $peer_tunnel_esp_group = $vcVPN->returnValue("ipsec site-to-site peer $peer tunnel $tunnel esp-group"); - if (!defined($peer_tunnel_esp_group) || $peer_tunnel_esp_group eq '') { - $error = 1; - print STDERR "VPN configuration error. No ESP group specified for peer \"$peer\" tunnel $tunnel.\n"; - } elsif (!$vcVPN->exists("ipsec esp-group $peer_tunnel_esp_group")) { - $error = 1; - print STDERR "VPN configuration error. The ESP group \"$peer_tunnel_esp_group\" specified for peer \"$peer\" tunnel $tunnel has not been configured.\n"; - } - - my $conn_head = "\nconn peer-$peer-tunnel-$tunnel\n"; - $conn_head =~ s/ peer-@/ peer-/; - $genout .= $conn_head; - - # - # Assign left and right to local and remote interfaces - # - if (defined($lip)) { - if ($lip eq '0.0.0.0') { - if (!defined($authid)) { - print STDERR 'VPN configuration error. ' - . 'The "authentication id" must be ' - . 'configured if local IP is 0.0.0.0.' - . "\n"; - $error = 1; - } else { - $genout .= "\tleft=%defaultroute\n"; - $genout .= "\tleftid=$authid\n"; - } - } else { - $genout .= "\tleft=$lip\n"; - } - } - - my $any_peer = 0; - my $right; - my $rightid = undef; - if ($peer =~ /^\@/) { - # peer is an "ID" - $rightid = $peer; - $any_peer = 1; - } elsif ($authremoteid) { - $rightid = $authremoteid; - } - if (($peer eq 'any') or ($peer eq '0.0.0.0') - or $any_peer == 1) { - $right = '%any'; - } else { - $right = $peer; - } - $genout .= "\tright=$right\n"; - $genout .= "\trightid=$rightid\n" if (defined($rightid)); - if ($any_peer) { - $genout .= "\trekey=no\n"; - } - - # - # Write tunnel configuration - # - my $leftsubnet = $vcVPN->returnValue("ipsec site-to-site peer $peer tunnel $tunnel local-subnet"); - if (defined($leftsubnet) && $leftsubnet eq 'any') { - $leftsubnet = '0.0.0.0/0'; - } - if (defined($leftsubnet)) { - $genout .= "\tleftsubnet=$leftsubnet\n"; - } - - my $remotesubnet = $vcVPN->returnValue("ipsec site-to-site peer $peer tunnel $tunnel remote-subnet"); - - my $rightsubnet; - my $allow_nat_networks = $vcVPN->returnValue("ipsec site-to-site peer $peer tunnel $tunnel allow-nat-networks"); - my $allow_public_networks = $vcVPN->returnValue("ipsec site-to-site peer $peer tunnel $tunnel allow-public-networks"); - - if (defined($allow_nat_networks) && $allow_nat_networks eq 'enable') { - if (defined($remotesubnet) && $remotesubnet ne "") { - $error = 1; - print STDERR "VPN configuration error. The 'remote-subnet' has been specified while 'allow-nat-networks' has been enabled for peer \"$peer\" tunnel $tunnel. Both not allowed at once.\n"; - } - - my @allowed_network = $vcVPN->listNodes('ipsec nat-networks allowed-network'); - if (@allowed_network == 0) { - $error = 1; - print STDERR "VPN configuration error. While 'allow-nat-networks' has been enabled for peer \"$peer\" tunnel $tunnel, no global allowed NAT networks have been configured.\n"; - } - - $rightsubnet = "%priv"; - if (defined($allow_public_networks) && $allow_public_networks eq "enable") { - if (defined($remotesubnet) && $remotesubnet ne "") { - $error = 1; - print STDERR "VPN configuration error. The 'remote-subnet' has been specified while 'allow-public-networks' has been enabled for peer \"$peer\" tunnel $tunnel. Both not allowed at once.\n"; - } - $rightsubnet .= ",%no"; - } - } else { - $rightsubnet = $remotesubnet; - if (defined($rightsubnet) && $rightsubnet eq 'any') { - $rightsubnet = '0.0.0.0/0'; - } - } - if (defined($rightsubnet)) { - $genout .= "\trightsubnet=$rightsubnet\n"; - } - - # - # Write IKE configuration from group - # - my $ikelifetime = IKELIFETIME_DEFAULT; - $genout .= "\tike="; - my $ike_group = $vcVPN->returnValue("ipsec site-to-site peer $peer ike-group"); - if (defined($ike_group) && $ike_group ne '') { - my @ike_proposals = $vcVPN->listNodes("ipsec ike-group $ike_group proposal"); - - my $first_ike_proposal = 1; - foreach my $ike_proposal (@ike_proposals) { - # - # Get encryption, hash & Diffie-Hellman key size - # - my $encryption = $vcVPN->returnValue("ipsec ike-group $ike_group proposal $ike_proposal encryption"); - my $hash = $vcVPN->returnValue("ipsec ike-group $ike_group proposal $ike_proposal hash"); - my $dh_group = $vcVPN->returnValue("ipsec ike-group $ike_group proposal $ike_proposal dh-group"); - - # - # Write separator if not first proposal - # - if ($first_ike_proposal) { - $first_ike_proposal = 0; - } else { - $genout .= ","; - } - - # - # Write values - # - if (defined($encryption) && defined($hash)) { - $genout .= "$encryption-$hash"; - if (defined($dh_group)) { - if ($dh_group eq '2') { - $genout .= '-modp1024'; - } elsif ($dh_group eq '5') { - $genout .= '-modp1536'; - } elsif ($dh_group ne '') { - $error = 1; - print STDERR "VPN configuration error. Invalid 'dh-group' $dh_group specified for peer \"$peer\" tunnel $tunnel. Only 2 or 5 accepted.\n"; - } - } - } - } - $genout .= "\n"; - - my $t_ikelifetime = $vcVPN->returnValue("ipsec ike-group $ike_group lifetime"); - if (defined($t_ikelifetime) && $t_ikelifetime ne '') { - $ikelifetime = $t_ikelifetime; - } - $genout .= "\tikelifetime=$ikelifetime" . "s\n"; - - # - # Check for agressive-mode - # - my $aggressive_mode = $vcVPN->returnValue("ipsec ike-group $ike_group aggressive-mode"); - if (defined($aggressive_mode)) { - if ($aggressive_mode eq 'enable') { - $genout .= "\taggrmode=yes\n"; - } else { - $genout .= "\taggrmode=no\n"; - } - } - - - # - # Check for Dead Peer Detection DPD - # - my $dpd_interval = $vcVPN->returnValue("ipsec ike-group $ike_group dead-peer-detection interval"); - my $dpd_timeout = $vcVPN->returnValue("ipsec ike-group $ike_group dead-peer-detection timeout"); - my $dpd_action = $vcVPN->returnValue("ipsec ike-group $ike_group dead-peer-detection action"); - if (defined($dpd_interval) && defined($dpd_timeout) && defined($dpd_action)) { - $genout .= "\tdpddelay=$dpd_interval" . "s\n"; - $genout .= "\tdpdtimeout=$dpd_timeout" . "s\n"; - $genout .= "\tdpdaction=$dpd_action\n"; - } - } - - # - # Write ESP configuration from group - # - my $esplifetime = ESPLIFETIME_DEFAULT; - $genout .= "\tesp="; - my $esp_group = $vcVPN->returnValue("ipsec site-to-site peer $peer tunnel $tunnel esp-group"); - if (defined($esp_group) && $esp_group ne '') { - my @esp_proposals = $vcVPN->listNodes("ipsec esp-group $esp_group proposal"); - my $first_esp_proposal = 1; - foreach my $esp_proposal (@esp_proposals) { - # - # Get encryption, hash - # - my $encryption = $vcVPN->returnValue("ipsec esp-group $esp_group proposal $esp_proposal encryption"); - my $hash = $vcVPN->returnValue("ipsec esp-group $esp_group proposal $esp_proposal hash"); - - # - # Write separator if not first proposal - # - if ($first_esp_proposal) { - $first_esp_proposal = 0; - } else { - $genout .= ","; - } - - # - # Write values - # - if (defined($encryption) && defined($hash)) { - $genout .= "$encryption-$hash"; - } - } - $genout .= "\n"; - - my $t_esplifetime = $vcVPN->returnValue("ipsec esp-group $esp_group lifetime"); - if (defined($t_esplifetime) && $t_esplifetime ne '') { - $esplifetime = $t_esplifetime; - } - $genout .= "\tkeylife=$esplifetime" . "s\n"; - - my $lower_lifetime = $ikelifetime; - if ($esplifetime < $ikelifetime) { - $lower_lifetime = $esplifetime; - } - - # - # The lifetime values need to be greater than: - # rekeymargin*(100+rekeyfuzz)/100 - # - my $rekeymargin = REKEYMARGIN_DEFAULT; - if ($lower_lifetime <= (2 * $rekeymargin)) { - $rekeymargin = int($lower_lifetime / 2) - 1; - } - $genout .= "\trekeymargin=$rekeymargin" . "s\n"; - - # - # Mode (tunnel or transport) - # - my $espmode = $vcVPN->returnValue("ipsec esp-group $esp_group mode"); - if (!defined($espmode) || $espmode eq '') { - $espmode = "tunnel"; - } - if ($espmode eq "transport") { - if (defined $leftsubnet or defined $rightsubnet) { - $error = 1; - print STDERR "VPN configuration error. Can not use local-subnet or remote-subnet when using transport mode\n"; - } - } - $genout .= "\ttype=$espmode\n"; - - # - # Perfect Forward Secrecy - # - my $pfs = $vcVPN->returnValue("ipsec esp-group $esp_group pfs"); - if (defined($pfs)) { - if ($pfs eq 'enable') { - $genout .= "\tpfs=yes\n"; - } else { - $genout .= "\tpfs=no\n"; - } - } - - # - # Compression - # - my $compression = $vcVPN->returnValue("ipsec esp-group $esp_group compression"); - if (defined($compression)) { - if ($compression eq 'enable') { - $genout .= "\tcompress=yes\n"; - } else { - $genout .= "\tcompress=no\n"; - } - } - } - - # - # Authentication mode - # - # - # Write shared secrets to ipsec.secrets - # - my $auth_mode = $vcVPN->returnValue("ipsec site-to-site peer $peer authentication mode"); - if (!defined($auth_mode) || $auth_mode eq '') { - $error = 1; - print STDERR "VPN configuration error. No authentication mode for peer \"$peer\" specified.\n"; - } elsif (defined($auth_mode) && ($auth_mode eq 'pre-shared-secret')) { - my $psk = $vcVPN->returnValue("ipsec site-to-site peer $peer authentication pre-shared-secret"); - if (!defined($psk) || $psk eq '') { - $error = 1; - print STDERR "VPN configuration error. No 'pre-shared-secret' specified for peer \"$peer\" while 'pre-shared-secret' authentication mode is specified.\n"; - } - - my $right; - if (($peer eq 'any') or ($peer eq '0.0.0.0') - or ($peer =~ /^\@/)) { - $right = '%any'; - if (defined($wildcard_psk)) { - if ($wildcard_psk ne $psk) { - $error = 1; - print STDERR 'VPN configuration error. ' - . 'All dynamic peers must have the same ' - . "'pre-shared-secret'.\n"; - } - } else { - $wildcard_psk = $psk; - } - } else { - $right = $peer; - } - if (defined $lip) { - my $index1 = ($lip eq '0.0.0.0' && defined($authid)) - ? "$authid" : $lip; - $genout_secrets .= "$index1 $right : PSK \"$psk\"\n"; - } - if (defined($lip) and defined($authremoteid)) { - $genout_secrets .= "$lip $authremoteid : PSK \"$psk\"\n"; - } - $genout .= "\tauthby=secret\n"; - } elsif (defined($auth_mode) && $auth_mode eq 'rsa') { - - unless (-r $local_key_file) { - $error = 1; - if (-e $local_key_file) { - print STDERR "VPN configuration error. Invalid local RSA key file path \"$local_key_file\". Filesystem read permission absent.\n"; - } else { - print STDERR "VPN configuration error. Invalid local RSA key file path \"$local_key_file\". File absent. Use the 'vpn rsa-key generate' command to create.\n"; - } - } - - $genout .= "\tauthby=rsasig\n"; - my $local_key = rsa_get_local_pubkey($local_key_file); - if (!defined($local_key) || $local_key eq "") { - $error = 1; - print STDERR "VPN configuration error. Unable to determine local public key from local key file \"$local_key_file\" for peer \"$peer\".\n"; - } else { - $genout .= "\tleftrsasigkey=$local_key\n"; - } - - my $rsa_key_name = $vcVPN->returnValue("ipsec site-to-site peer $peer authentication rsa-key-name"); - if (!defined($rsa_key_name) || $rsa_key_name eq "") { - $error = 1; - print STDERR "VPN configuration error. No 'rsa-key-name' specified for peer \"$peer\" while 'rsa' authentication mode is specified.\n"; - } else { - my $remote_key = $vcVPN->returnValue("rsa-keys rsa-key-name $rsa_key_name rsa-key"); - if (!defined($remote_key) || $remote_key eq "") { - $error = 1; - print STDERR "VPN configuration error. No remote key configured for rsa key name \"$rsa_key_name\" that is specified for peer \"$peer\".\n"; - } else { - $genout .= "\trightrsasigkey=$remote_key\n"; - } - } - $genout_secrets .= "include $local_key_file\n"; - } else { - $error = 1; - print STDERR "VPN configuration error. Unknown authentication mode \"$auth_mode\" for peer \"$peer\" specified.\n"; - } - - # - # Start automatically - # - if ($any_peer) { - $genout .= "\tauto=add\n"; - } else { - $genout .= "\tauto=start\n"; - } - } + foreach my $tunnel (@tunnels) { + + # + # Add support for tunnel disable. + # + if ( + $vcVPN->exists("ipsec site-to-site peer $peer tunnel $tunnel disable") ) + { + next; + } + + my $peer_tunnel_esp_group = $vcVPN->returnValue( + "ipsec site-to-site peer $peer tunnel $tunnel esp-group"); + if ( !defined($peer_tunnel_esp_group) || $peer_tunnel_esp_group eq '' ) { + $error = 1; + print STDERR +"VPN configuration error. No ESP group specified for peer \"$peer\" tunnel $tunnel.\n"; + } elsif ( !$vcVPN->exists("ipsec esp-group $peer_tunnel_esp_group") ) { + $error = 1; + print STDERR +"VPN configuration error. The ESP group \"$peer_tunnel_esp_group\" specified for peer \"$peer\" tunnel $tunnel has not been configured.\n"; + } + + my $conn_head = "\nconn peer-$peer-tunnel-$tunnel\n"; + $conn_head =~ s/ peer-@/ peer-/; + $genout .= $conn_head; + + # + # Assign left and right to local and remote interfaces + # + if ( defined($lip) ) { + if ( $lip eq '0.0.0.0' ) { + if ( !defined($authid) ) { + print STDERR 'VPN configuration error. ' + . 'The "authentication id" must be ' + . 'configured if local IP is 0.0.0.0.' . "\n"; + $error = 1; + } else { + $genout .= "\tleft=%defaultroute\n"; + $genout .= "\tleftid=$authid\n"; + } + } else { + $genout .= "\tleft=$lip\n"; + } + } + + my $any_peer = 0; + my $right; + my $rightid = undef; + if ( $peer =~ /^\@/ ) { + + # peer is an "ID" + $rightid = $peer; + $any_peer = 1; + } elsif ($authremoteid) { + $rightid = $authremoteid; + } + if ( ( $peer eq 'any' ) + or ( $peer eq '0.0.0.0' ) + or $any_peer == 1 ) + { + $right = '%any'; + } else { + $right = $peer; + } + $genout .= "\tright=$right\n"; + $genout .= "\trightid=$rightid\n" if ( defined($rightid) ); + if ($any_peer) { + $genout .= "\trekey=no\n"; + } + + # + # Write tunnel configuration + # + my $leftsubnet = $vcVPN->returnValue( + "ipsec site-to-site peer $peer tunnel $tunnel local-subnet"); + if ( defined($leftsubnet) && $leftsubnet eq 'any' ) { + $leftsubnet = '0.0.0.0/0'; + } + if ( defined($leftsubnet) ) { + $genout .= "\tleftsubnet=$leftsubnet\n"; + } + + my $remotesubnet = $vcVPN->returnValue( + "ipsec site-to-site peer $peer tunnel $tunnel remote-subnet"); + + my $rightsubnet; + my $allow_nat_networks = $vcVPN->returnValue( + "ipsec site-to-site peer $peer tunnel $tunnel allow-nat-networks"); + my $allow_public_networks = $vcVPN->returnValue( + "ipsec site-to-site peer $peer tunnel $tunnel allow-public-networks"); + + if ( defined($allow_nat_networks) && $allow_nat_networks eq 'enable' ) { + if ( defined($remotesubnet) && $remotesubnet ne "" ) { + $error = 1; + print STDERR +"VPN configuration error. The 'remote-subnet' has been specified while 'allow-nat-networks' has been enabled for peer \"$peer\" tunnel $tunnel. Both not allowed at once.\n"; + } + + my @allowed_network = + $vcVPN->listNodes('ipsec nat-networks allowed-network'); + if ( @allowed_network == 0 ) { + $error = 1; + print STDERR +"VPN configuration error. While 'allow-nat-networks' has been enabled for peer \"$peer\" tunnel $tunnel, no global allowed NAT networks have been configured.\n"; + } + + $rightsubnet = "%priv"; + if ( defined($allow_public_networks) + && $allow_public_networks eq "enable" ) + { + if ( defined($remotesubnet) && $remotesubnet ne "" ) { + $error = 1; + print STDERR +"VPN configuration error. The 'remote-subnet' has been specified while 'allow-public-networks' has been enabled for peer \"$peer\" tunnel $tunnel. Both not allowed at once.\n"; + } + $rightsubnet .= ",%no"; + } + } else { + $rightsubnet = $remotesubnet; + if ( defined($rightsubnet) && $rightsubnet eq 'any' ) { + $rightsubnet = '0.0.0.0/0'; + } + } + if ( defined($rightsubnet) ) { + $genout .= "\trightsubnet=$rightsubnet\n"; + } + + # + # Write IKE configuration from group + # + my $ikelifetime = IKELIFETIME_DEFAULT; + $genout .= "\tike="; + my $ike_group = + $vcVPN->returnValue("ipsec site-to-site peer $peer ike-group"); + if ( defined($ike_group) && $ike_group ne '' ) { + my @ike_proposals = + $vcVPN->listNodes("ipsec ike-group $ike_group proposal"); + + my $first_ike_proposal = 1; + foreach my $ike_proposal (@ike_proposals) { + + # + # Get encryption, hash & Diffie-Hellman key size + # + my $encryption = $vcVPN->returnValue( + "ipsec ike-group $ike_group proposal $ike_proposal encryption"); + my $hash = $vcVPN->returnValue( + "ipsec ike-group $ike_group proposal $ike_proposal hash"); + my $dh_group = $vcVPN->returnValue( + "ipsec ike-group $ike_group proposal $ike_proposal dh-group"); + + # + # Write separator if not first proposal + # + if ($first_ike_proposal) { + $first_ike_proposal = 0; + } else { + $genout .= ","; + } + + # + # Write values + # + if ( defined($encryption) && defined($hash) ) { + $genout .= "$encryption-$hash"; + if ( defined($dh_group) ) { + if ( $dh_group eq '2' ) { + $genout .= '-modp1024'; + } elsif ( $dh_group eq '5' ) { + $genout .= '-modp1536'; + } elsif ( $dh_group ne '' ) { + $error = 1; + print STDERR +"VPN configuration error. Invalid 'dh-group' $dh_group specified for peer \"$peer\" tunnel $tunnel. Only 2 or 5 accepted.\n"; + } + } + } + } + $genout .= "\n"; + + my $t_ikelifetime = + $vcVPN->returnValue("ipsec ike-group $ike_group lifetime"); + if ( defined($t_ikelifetime) && $t_ikelifetime ne '' ) { + $ikelifetime = $t_ikelifetime; + } + $genout .= "\tikelifetime=$ikelifetime" . "s\n"; + + # + # Check for agressive-mode + # + my $aggressive_mode = + $vcVPN->returnValue("ipsec ike-group $ike_group aggressive-mode"); + if ( defined($aggressive_mode) ) { + if ( $aggressive_mode eq 'enable' ) { + $genout .= "\taggrmode=yes\n"; + } else { + $genout .= "\taggrmode=no\n"; + } + } + + # + # Check for Dead Peer Detection DPD + # + my $dpd_interval = $vcVPN->returnValue( + "ipsec ike-group $ike_group dead-peer-detection interval"); + my $dpd_timeout = $vcVPN->returnValue( + "ipsec ike-group $ike_group dead-peer-detection timeout"); + my $dpd_action = $vcVPN->returnValue( + "ipsec ike-group $ike_group dead-peer-detection action"); + if ( defined($dpd_interval) + && defined($dpd_timeout) + && defined($dpd_action) ) + { + $genout .= "\tdpddelay=$dpd_interval" . "s\n"; + $genout .= "\tdpdtimeout=$dpd_timeout" . "s\n"; + $genout .= "\tdpdaction=$dpd_action\n"; + } + } + + # + # Write ESP configuration from group + # + my $esplifetime = ESPLIFETIME_DEFAULT; + $genout .= "\tesp="; + my $esp_group = $vcVPN->returnValue( + "ipsec site-to-site peer $peer tunnel $tunnel esp-group"); + if ( defined($esp_group) && $esp_group ne '' ) { + my @esp_proposals = + $vcVPN->listNodes("ipsec esp-group $esp_group proposal"); + my $first_esp_proposal = 1; + foreach my $esp_proposal (@esp_proposals) { + + # + # Get encryption, hash + # + my $encryption = $vcVPN->returnValue( + "ipsec esp-group $esp_group proposal $esp_proposal encryption"); + my $hash = $vcVPN->returnValue( + "ipsec esp-group $esp_group proposal $esp_proposal hash"); + + # + # Write separator if not first proposal + # + if ($first_esp_proposal) { + $first_esp_proposal = 0; + } else { + $genout .= ","; + } + + # + # Write values + # + if ( defined($encryption) && defined($hash) ) { + $genout .= "$encryption-$hash"; + } + } + $genout .= "\n"; + + my $t_esplifetime = + $vcVPN->returnValue("ipsec esp-group $esp_group lifetime"); + if ( defined($t_esplifetime) && $t_esplifetime ne '' ) { + $esplifetime = $t_esplifetime; + } + $genout .= "\tkeylife=$esplifetime" . "s\n"; + + my $lower_lifetime = $ikelifetime; + if ( $esplifetime < $ikelifetime ) { + $lower_lifetime = $esplifetime; + } + + # + # The lifetime values need to be greater than: + # rekeymargin*(100+rekeyfuzz)/100 + # + my $rekeymargin = REKEYMARGIN_DEFAULT; + if ( $lower_lifetime <= ( 2 * $rekeymargin ) ) { + $rekeymargin = int( $lower_lifetime / 2 ) - 1; + } + $genout .= "\trekeymargin=$rekeymargin" . "s\n"; + + # + # Mode (tunnel or transport) + # + my $espmode = $vcVPN->returnValue("ipsec esp-group $esp_group mode"); + if ( !defined($espmode) || $espmode eq '' ) { + $espmode = "tunnel"; + } + if ( $espmode eq "transport" ) { + if ( defined $leftsubnet or defined $rightsubnet ) { + $error = 1; + print STDERR +"VPN configuration error. Can not use local-subnet or remote-subnet when using transport mode\n"; + } + } + $genout .= "\ttype=$espmode\n"; + + # + # Perfect Forward Secrecy + # + my $pfs = $vcVPN->returnValue("ipsec esp-group $esp_group pfs"); + if ( defined($pfs) ) { + if ( $pfs eq 'enable' ) { + $genout .= "\tpfs=yes\n"; + } else { + $genout .= "\tpfs=no\n"; + } + } + + # + # Compression + # + my $compression = + $vcVPN->returnValue("ipsec esp-group $esp_group compression"); + if ( defined($compression) ) { + if ( $compression eq 'enable' ) { + $genout .= "\tcompress=yes\n"; + } else { + $genout .= "\tcompress=no\n"; + } + } + } + + # + # Authentication mode + # + # + # Write shared secrets to ipsec.secrets + # + my $auth_mode = $vcVPN->returnValue( + "ipsec site-to-site peer $peer authentication mode"); + if ( !defined($auth_mode) || $auth_mode eq '' ) { + $error = 1; + print STDERR +"VPN configuration error. No authentication mode for peer \"$peer\" specified.\n"; + } elsif ( defined($auth_mode) && ( $auth_mode eq 'pre-shared-secret' ) ) { + my $psk = $vcVPN->returnValue( + "ipsec site-to-site peer $peer authentication pre-shared-secret"); + if ( !defined($psk) || $psk eq '' ) { + $error = 1; + print STDERR +"VPN configuration error. No 'pre-shared-secret' specified for peer \"$peer\" while 'pre-shared-secret' authentication mode is specified.\n"; + } + + my $right; + if ( ( $peer eq 'any' ) + or ( $peer eq '0.0.0.0' ) + or ( $peer =~ /^\@/ ) ) + { + $right = '%any'; + if ( defined($wildcard_psk) ) { + if ( $wildcard_psk ne $psk ) { + $error = 1; + print STDERR 'VPN configuration error. ' + . 'All dynamic peers must have the same ' + . "'pre-shared-secret'.\n"; + } + } else { + $wildcard_psk = $psk; + } + } else { + $right = $peer; + } + if ( defined $lip ) { + my $index1 = + ( $lip eq '0.0.0.0' && defined($authid) ) ? "$authid" : $lip; + $genout_secrets .= "$index1 $right : PSK \"$psk\"\n"; + } + if ( defined($lip) and defined($authremoteid) ) { + $genout_secrets .= "$lip $authremoteid : PSK \"$psk\"\n"; + } + $genout .= "\tauthby=secret\n"; + } elsif ( defined($auth_mode) && $auth_mode eq 'rsa' ) { + + unless ( -r $local_key_file ) { + $error = 1; + if ( -e $local_key_file ) { + print STDERR +"VPN configuration error. Invalid local RSA key file path \"$local_key_file\". Filesystem read permission absent.\n"; + } else { + print STDERR +"VPN configuration error. Invalid local RSA key file path \"$local_key_file\". File absent. Use the 'vpn rsa-key generate' command to create.\n"; + } + } + + $genout .= "\tauthby=rsasig\n"; + my $local_key = rsa_get_local_pubkey($local_key_file); + if ( !defined($local_key) || $local_key eq "" ) { + $error = 1; + print STDERR +"VPN configuration error. Unable to determine local public key from local key file \"$local_key_file\" for peer \"$peer\".\n"; + } else { + $genout .= "\tleftrsasigkey=$local_key\n"; + } + + my $rsa_key_name = $vcVPN->returnValue( + "ipsec site-to-site peer $peer authentication rsa-key-name"); + if ( !defined($rsa_key_name) || $rsa_key_name eq "" ) { + $error = 1; + print STDERR +"VPN configuration error. No 'rsa-key-name' specified for peer \"$peer\" while 'rsa' authentication mode is specified.\n"; + } else { + my $remote_key = + $vcVPN->returnValue("rsa-keys rsa-key-name $rsa_key_name rsa-key"); + if ( !defined($remote_key) || $remote_key eq "" ) { + $error = 1; + print STDERR +"VPN configuration error. No remote key configured for rsa key name \"$rsa_key_name\" that is specified for peer \"$peer\".\n"; + } else { + $genout .= "\trightrsasigkey=$remote_key\n"; + } + } + $genout_secrets .= "include $local_key_file\n"; + } else { + $error = 1; + print STDERR +"VPN configuration error. Unknown authentication mode \"$auth_mode\" for peer \"$peer\" specified.\n"; + } + + # + # Start automatically + # + if ($any_peer) { + $genout .= "\tauto=add\n"; + } else { + $genout .= "\tauto=start\n"; + } } + } } else { - # - # remove any previous config lines, so that when "clear vpn ipsec-process" - # is called it won't find the vyatta keyword and therefore will not try - # to start the ipsec process. - # - $genout = ''; - $genout .= "# No VPN configuration exists.\n"; - $genout_secrets .= "# No VPN configuration exists.\n"; + + # + # remove any previous config lines, so that when "clear vpn ipsec-process" + # is called it won't find the vyatta keyword and therefore will not try + # to start the ipsec process. + # + $genout = ''; + $genout .= "# No VPN configuration exists.\n"; + $genout_secrets .= "# No VPN configuration exists.\n"; } -if (!(defined($config_file) && ($config_file ne '') && defined($secrets_file) && ($secrets_file ne ''))) { - print "Regular config file output would be:\n\n$genout\n\n"; - print "Secrets config file output would be:\n\n$genout_secrets\n\n"; - exit (0); +if ( + !( + defined($config_file) + && ( $config_file ne '' ) + && defined($secrets_file) + && ( $secrets_file ne '' ) + ) + ) +{ + print "Regular config file output would be:\n\n$genout\n\n"; + print "Secrets config file output would be:\n\n$genout_secrets\n\n"; + exit(0); } +if ( $error == 0 ) { + if ( $vcVPN->isDeleted('.') + || !$vcVPN->exists('.') + || $vcVPN->isDeleted('ipsec') + || !$vcVPN->exists('ipsec') ) + { + if ( Vyatta::Misc::isClusterIP( $vc, 'ipsec' ) ) { + $error = 1; + print STDERR + "VPN commit error. Cluster service is referencing ipsec config.\n"; + } + if ( $error == 0 ) { + if ( is_vpn_running() ) { + vpn_exec( 'ipsec setup --stop', 'stop ipsec' ); + } + if ( !enableICMP('1') ) { + $error = 1; + print STDERR "VPN commit error. Unable to re-enable ICMP redirects.\n"; + } + write_config( $genout, $config_file, $genout_secrets, $secrets_file ); + } + } else { + if ( !enableICMP('0') ) { + $error = 1; + print STDERR "VPN commit error. Unable to disable ICMP redirects.\n"; + } -if ($error == 0) { - if ($vcVPN->isDeleted('.') || !$vcVPN->exists('.') - || $vcVPN->isDeleted('ipsec') || !$vcVPN->exists('ipsec')) { - if (Vyatta::Misc::isClusterIP($vc, 'ipsec')) { - $error = 1; - print STDERR "VPN commit error. Cluster service is referencing ipsec config.\n"; - } - if ($error == 0) { - if (is_vpn_running()) { - vpn_exec('ipsec setup --stop', 'stop ipsec'); - } - if (!enableICMP('1')) { - $error = 1; - print STDERR "VPN commit error. Unable to re-enable ICMP redirects.\n"; - } - write_config($genout, $config_file, $genout_secrets, $secrets_file); - } + # Assumming that if there was a local IP missmatch and clustering is enabled, + # then the clustering scripts will take care of starting the VPN daemon. + if ($clustering_ip) { + +# If the local-ip is provided by clustering, then just write out the configuration, +# but do not start the VPN daemon + + write_config( $genout, $config_file, $genout_secrets, $secrets_file ); + + vpn_log( +"Wrote out configuration to files '$config_file' and '$secrets_file'. VPN/ipsec daemons not started due to clustering.\n" + ); + print "Clustering configured - not restarting ipsec\n"; } else { - if (!enableICMP('0')) { - $error = 1; - print STDERR "VPN commit error. Unable to disable ICMP redirects.\n"; - } - # Assumming that if there was a local IP missmatch and clustering is enabled, - # then the clustering scripts will take care of starting the VPN daemon. - if ($clustering_ip) { - # If the local-ip is provided by clustering, then just write out the configuration, - # but do not start the VPN daemon - - write_config($genout, $config_file, $genout_secrets, $secrets_file); - - vpn_log("Wrote out configuration to files '$config_file' and '$secrets_file'. VPN/ipsec daemons not started due to clustering.\n"); - print "Clustering configured - not restarting ipsec\n"; - } else { - if (is_vpn_running()) { - if (isFullRestartRequired($vcVPN)) { - # - # Full restart required - # - write_config($genout, $config_file, $genout_secrets, $secrets_file); - vpn_exec('ipsec setup --restart', 'restart ipsec'); - } else { - my @conn_down; - my @conn_delete; - my @conn_replace; - my @conn_add; - my @conn_up; - partial_restart($vcVPN, \@conn_down, \@conn_delete, \@conn_replace, \@conn_add, \@conn_up); - - foreach my $conn (@conn_down) { - vpn_exec("ipsec auto --down $conn", "bring down ipsec connection $conn"); - } - foreach my $conn (@conn_delete) { - vpn_exec("ipsec auto --delete $conn", "delete ipsec connection $conn"); - } - - write_config($genout, $config_file, $genout_secrets, $secrets_file); - vpn_exec('ipsec auto --rereadall', 're-read ipsec configuration'); - - foreach my $conn (@conn_replace) { - vpn_exec("ipsec auto --replace $conn", "replace ipsec connection $conn"); - } - foreach my $conn (@conn_add) { - vpn_exec("ipsec auto --add $conn", "add ipsec connection $conn"); - } - foreach my $conn (@conn_up) { - vpn_exec("ipsec auto --asynchronous --up $conn", "bring up replaced ipsec connection $conn"); - } - - } - } else { - write_config($genout, $config_file, $genout_secrets, $secrets_file); - vpn_exec('ipsec setup --start', 'start ipsec'); - } - } + if ( is_vpn_running() ) { + if ( isFullRestartRequired($vcVPN) ) { + + # + # Full restart required + # + write_config( $genout, $config_file, $genout_secrets, $secrets_file ); + vpn_exec( 'ipsec setup --restart', 'restart ipsec' ); + } else { + my @conn_down; + my @conn_delete; + my @conn_replace; + my @conn_add; + my @conn_up; + partial_restart( $vcVPN, \@conn_down, \@conn_delete, \@conn_replace, + \@conn_add, \@conn_up ); + + foreach my $conn (@conn_down) { + vpn_exec( "ipsec auto --down $conn", + "bring down ipsec connection $conn" ); + } + foreach my $conn (@conn_delete) { + vpn_exec( "ipsec auto --delete $conn", + "delete ipsec connection $conn" ); + } + + write_config( $genout, $config_file, $genout_secrets, $secrets_file ); + vpn_exec( 'ipsec auto --rereadall', 're-read ipsec configuration' ); + + foreach my $conn (@conn_replace) { + vpn_exec( + "ipsec auto --replace $conn", + "replace ipsec connection $conn" + ); + } + foreach my $conn (@conn_add) { + vpn_exec( "ipsec auto --add $conn", "add ipsec connection $conn" ); + } + foreach my $conn (@conn_up) { + vpn_exec( + "ipsec auto --asynchronous --up $conn", + "bring up replaced ipsec connection $conn" + ); + } + + } + } else { + write_config( $genout, $config_file, $genout_secrets, $secrets_file ); + vpn_exec( 'ipsec setup --start', 'start ipsec' ); + } } + } } - # # If error return error # if ($error) { - print STDERR "VPN configuration commit aborted due to error(s).\n"; - exit(1); + print STDERR "VPN configuration commit aborted due to error(s).\n"; + exit(1); } - # # Return success # exit 0; - sub write_config { - my ($genout, $config_file, $genout_secrets, $secrets_file) = @_; - - open my $output_config, '>', $config_file - or die "Can't open $config_file: $!"; - print ${output_config} $genout; - close $output_config; - - open my $output_secrets, '>', $secrets_file - or die "Can't open $secrets_file: $!"; - print ${output_secrets} $genout_secrets; - close $output_secrets; + my ( $genout, $config_file, $genout_secrets, $secrets_file ) = @_; + + open my $output_config, '>', $config_file + or die "Can't open $config_file: $!"; + print ${output_config} $genout; + close $output_config; + + open my $output_secrets, '>', $secrets_file + or die "Can't open $secrets_file: $!"; + print ${output_secrets} $genout_secrets; + close $output_secrets; } sub partial_restart { - my ($vcVPN, $conn_down, $conn_delete, $conn_replace, $conn_add, $conn_up) = @_; - - my $debug = 0; - - # - # - # Print configuration trees if debug enabled - # - - if ($debug) { - print "Modified configuration:\n"; - printTree(); - print "\n"; - print "\nUnmodified configuration:\n"; - printTreeOrig(); - print "\n"; - } - - # - # Add and modify connections individually - # - my %peers = $vcVPN->listNodeStatus('ipsec site-to-site peer'); - while (my ($peer, $peer_status) = each %peers) { - - if ($peer_status eq 'added') { - my @tunnels = $vcVPN->listNodes("ipsec site-to-site peer $peer tunnel"); - foreach my $tunnel (@tunnels) { - addConnection($peer, $tunnel, $conn_add, $conn_up); - } - } elsif ($peer_status eq 'changed') { - my $restart_all_tunnels = 0; - if ($vcVPN->isChangedOrDeleted("ipsec site-to-site peer $peer authentication")) { - $restart_all_tunnels = 1; - } elsif ($vcVPN->isChangedOrDeleted("ipsec site-to-site peer $peer ike-group")) { - $restart_all_tunnels = 1; - } elsif ($vcVPN->isChangedOrDeleted("ipsec site-to-site peer $peer local-ip")) { - $restart_all_tunnels = 1; - } - my %tunnels = $vcVPN->listNodeStatus("ipsec site-to-site peer $peer tunnel"); - while (my ($tunnel, $tunnel_status) = each %tunnels) { - - #first identify state change in disable node - my $state; - my $cur_state_disable = $vcVPN->exists("ipsec site-to-site peer $peer tunnel $tunnel disable"); - my $old_state_disable = $vcVPN->listOrigNodes("ipsec site-to-site peer $peer tunnel $tunnel disable"); - if (defined $old_state_disable) { - $old_state_disable = 1; - } - else { - $old_state_disable = 0; - } - if ($cur_state_disable && $old_state_disable) { - #no change, disabled - #suppress any action - $state = 0; - } - elsif ($cur_state_disable && !$old_state_disable) { - #change, enabled->disabled - #change replace to delete - $state = 1; - } - elsif (!$cur_state_disable && $old_state_disable) { - #change, disabled->enabled - #change, change replace to add - $state = 2; - } - elsif (!$cur_state_disable && !$old_state_disable) { - #no change, enabled - #change, noop - $state = 3; - } - - my $conn = "peer-$peer-tunnel-$tunnel"; - $conn =~ s/peer-@/peer-/; - if ($tunnel_status eq 'added') { - if ($state == 0 || $state == 1) { - #noop - } - else { - addConnection($peer, $tunnel, $conn_add, $conn_up); - } - } elsif ($tunnel_status eq 'changed') { - if ($state == 1) { - deleteConnection($conn, $conn_down, $conn_delete); - } - elsif ($state == 2) { - addConnection($peer, $tunnel, $conn_add, $conn_up); - } - else { - replaceConnection($peer, $tunnel, $conn_down, $conn_replace, $conn_up); - } - } elsif ($tunnel_status eq 'deleted') { - deleteConnection($conn, $conn_down, $conn_delete); - } elsif ($tunnel_status eq 'static') { - if ($restart_all_tunnels || dependenciesChanged($vcVPN, $peer, $tunnel)) { - replaceConnection($peer, $tunnel, $conn_down, $conn_replace, $conn_up); - } - } - } - } elsif ($peer_status eq 'deleted') { - my @tunnels = $vcVPN->listOrigNodes("ipsec site-to-site peer $peer tunnel"); - foreach my $tunnel (@tunnels) { - my $conn = "peer-$peer-tunnel-$tunnel"; - $conn =~ s/peer-@/peer-/; - deleteConnection($conn, $conn_down, $conn_delete); - } - } elsif ($peer_status eq 'static') { - my @tunnels = $vcVPN->listNodes("ipsec site-to-site peer $peer tunnel"); - foreach my $tunnel (@tunnels) { - if (dependenciesChanged($vcVPN, $peer, $tunnel)) { - replaceConnection($peer, $tunnel, $conn_down, $conn_replace, $conn_up); - } - } - } + my ( $vcVPN, $conn_down, $conn_delete, $conn_replace, $conn_add, $conn_up ) = + @_; + + my $debug = 0; + + # + # + # Print configuration trees if debug enabled + # + + if ($debug) { + print "Modified configuration:\n"; + printTree(); + print "\n"; + print "\nUnmodified configuration:\n"; + printTreeOrig(); + print "\n"; + } + + # + # Add and modify connections individually + # + my %peers = $vcVPN->listNodeStatus('ipsec site-to-site peer'); + while ( my ( $peer, $peer_status ) = each %peers ) { + + if ( $peer_status eq 'added' ) { + my @tunnels = $vcVPN->listNodes("ipsec site-to-site peer $peer tunnel"); + foreach my $tunnel (@tunnels) { + addConnection( $peer, $tunnel, $conn_add, $conn_up ); + } + } elsif ( $peer_status eq 'changed' ) { + my $restart_all_tunnels = 0; + if ( + $vcVPN->isChangedOrDeleted( + "ipsec site-to-site peer $peer authentication") + ) + { + $restart_all_tunnels = 1; + } elsif ( + $vcVPN->isChangedOrDeleted("ipsec site-to-site peer $peer ike-group") ) + { + $restart_all_tunnels = 1; + } elsif ( + $vcVPN->isChangedOrDeleted("ipsec site-to-site peer $peer local-ip") ) + { + $restart_all_tunnels = 1; + } + my %tunnels = + $vcVPN->listNodeStatus("ipsec site-to-site peer $peer tunnel"); + while ( my ( $tunnel, $tunnel_status ) = each %tunnels ) { + + #first identify state change in disable node + my $state; + my $cur_state_disable = $vcVPN->exists( + "ipsec site-to-site peer $peer tunnel $tunnel disable"); + my $old_state_disable = $vcVPN->listOrigNodes( + "ipsec site-to-site peer $peer tunnel $tunnel disable"); + if ( defined $old_state_disable ) { + $old_state_disable = 1; + } else { + $old_state_disable = 0; + } + if ( $cur_state_disable && $old_state_disable ) { + + #no change, disabled + #suppress any action + $state = 0; + } elsif ( $cur_state_disable && !$old_state_disable ) { + + #change, enabled->disabled + #change replace to delete + $state = 1; + } elsif ( !$cur_state_disable && $old_state_disable ) { + + #change, disabled->enabled + #change, change replace to add + $state = 2; + } elsif ( !$cur_state_disable && !$old_state_disable ) { + + #no change, enabled + #change, noop + $state = 3; + } + + my $conn = "peer-$peer-tunnel-$tunnel"; + $conn =~ s/peer-@/peer-/; + if ( $tunnel_status eq 'added' ) { + if ( $state == 0 || $state == 1 ) { + + #noop + } else { + addConnection( $peer, $tunnel, $conn_add, $conn_up ); + } + } elsif ( $tunnel_status eq 'changed' ) { + if ( $state == 1 ) { + deleteConnection( $conn, $conn_down, $conn_delete ); + } elsif ( $state == 2 ) { + addConnection( $peer, $tunnel, $conn_add, $conn_up ); + } else { + replaceConnection( $peer, $tunnel, $conn_down, $conn_replace, + $conn_up ); + } + } elsif ( $tunnel_status eq 'deleted' ) { + deleteConnection( $conn, $conn_down, $conn_delete ); + } elsif ( $tunnel_status eq 'static' ) { + if ( $restart_all_tunnels + || dependenciesChanged( $vcVPN, $peer, $tunnel ) ) + { + replaceConnection( $peer, $tunnel, $conn_down, $conn_replace, + $conn_up ); + } + } + } + } elsif ( $peer_status eq 'deleted' ) { + my @tunnels = + $vcVPN->listOrigNodes("ipsec site-to-site peer $peer tunnel"); + foreach my $tunnel (@tunnels) { + my $conn = "peer-$peer-tunnel-$tunnel"; + $conn =~ s/peer-@/peer-/; + deleteConnection( $conn, $conn_down, $conn_delete ); + } + } elsif ( $peer_status eq 'static' ) { + my @tunnels = $vcVPN->listNodes("ipsec site-to-site peer $peer tunnel"); + foreach my $tunnel (@tunnels) { + if ( dependenciesChanged( $vcVPN, $peer, $tunnel ) ) { + replaceConnection( $peer, $tunnel, $conn_down, $conn_replace, + $conn_up ); + } + } } + } } sub vpn_exec { - my ($command, $desc) = @_; - - if ($error != 0) { - return; + my ( $command, $desc ) = @_; + + if ( $error != 0 ) { + return; + } + + open my $logf, '>>', "/tmp/ipsec.log" + or die "Can't open /tmp/ipsec.log: $!"; + + use POSIX; + my $timestamp = strftime( "%Y-%m-%d %H:%M.%S", localtime ); + + print ${logf} "$timestamp\nExecuting: $command\nDescription: $desc\n"; + + if ( $error == 0 ) { + my $cmd_out = qx($command); + my $rval = ( $? >> 8 ); + print ${logf} "Output:\n$cmd_out\n---\n"; + print ${logf} "Return code: $rval\n"; + if ($rval) { + if ( $command =~ /^ipsec auto --asynchronous --up/ + && ( $rval == 104 || $rval == 29 ) ) + { + print ${logf} "OK when bringing up VPN connection\n"; + } else { + + # + # We use to consider the commit failed if we got a error + # from the call to ipsec, but this causes the configuration + # to not get included in the running config. Now that + # we support dynamic interface/address (e.g. dhcp, pppoe) + # we want a valid config to get committed even if the + # interface doesn't exist yet. That way we can use + # "clear vpn ipsec-process" to bring up the tunnel once + # the interface is instantiated. For pppoe we will add + # a script to /etc/ppp/ip-up.d to bring up the vpn + # tunnel. + # + print ${logf} + "VPN commit error. Unable to $desc, received error code $?\n"; + print "Warning: unable to [$desc], received error code $?\n"; + print "$cmd_out\n"; + } } + } else { + print ${logf} "Execution not performed due to previous error.\n"; + } - open my $logf, '>>', "/tmp/ipsec.log" - or die "Can't open /tmp/ipsec.log: $!"; - - use POSIX; - my $timestamp = strftime("%Y-%m-%d %H:%M.%S", localtime); - - print ${logf} "$timestamp\nExecuting: $command\nDescription: $desc\n"; - - if ($error == 0) { - my $cmd_out = qx($command); - my $rval = ($? >> 8); - print ${logf} "Output:\n$cmd_out\n---\n"; - print ${logf} "Return code: $rval\n"; - if ($rval) { - if ($command =~ /^ipsec auto --asynchronous --up/ - && ($rval == 104 || $rval == 29)) { - print ${logf} "OK when bringing up VPN connection\n"; - } else { - # - # We use to consider the commit failed if we got a error - # from the call to ipsec, but this causes the configuration - # to not get included in the running config. Now that - # we support dynamic interface/address (e.g. dhcp, pppoe) - # we want a valid config to get committed even if the - # interface doesn't exist yet. That way we can use - # "clear vpn ipsec-process" to bring up the tunnel once - # the interface is instantiated. For pppoe we will add - # a script to /etc/ppp/ip-up.d to bring up the vpn - # tunnel. - # - print ${logf} "VPN commit error. Unable to $desc, received error code $?\n"; - print "Warning: unable to [$desc], received error code $?\n"; - print "$cmd_out\n"; - } - } - } else { - print ${logf} "Execution not performed due to previous error.\n"; - } - - print ${logf} "---\n\n"; - close $logf; + print ${logf} "---\n\n"; + close $logf; } sub vpn_log { - my ($log) = @_; - - open my $logfile, '>>', "/tmp/ipsec.log" - or die "Can't open /tmp/ipsec.log: $!"; - - use POSIX; - my $timestamp = strftime("%Y-%m-%d %H:%M.%S", localtime); - - print ${logfile} "$timestamp\n$log\n"; - print ${logfile} "---\n\n"; - close $logfile; + my ($log) = @_; + + open my $logfile, '>>', "/tmp/ipsec.log" + or die "Can't open /tmp/ipsec.log: $!"; + + use POSIX; + my $timestamp = strftime( "%Y-%m-%d %H:%M.%S", localtime ); + + print ${logfile} "$timestamp\n$log\n"; + print ${logfile} "---\n\n"; + close $logfile; } sub addConnection { - my ($peer, $tunnel, $conn_add, $conn_up) = @_; - my $conn = "peer-$peer-tunnel-$tunnel"; - $conn =~ s/peer-@/peer-/; - push(@$conn_add, $conn); - if ($peer ne '0.0.0.0') { - push(@$conn_up, $conn); - } + my ( $peer, $tunnel, $conn_add, $conn_up ) = @_; + my $conn = "peer-$peer-tunnel-$tunnel"; + $conn =~ s/peer-@/peer-/; + push( @$conn_add, $conn ); + if ( $peer ne '0.0.0.0' ) { + push( @$conn_up, $conn ); + } } sub replaceConnection { - my ($peer, $tunnel, $conn_down, $conn_replace, $conn_up) = @_; - my $conn = "peer-$peer-tunnel-$tunnel"; - $conn =~ s/peer-@/peer-/; - push(@$conn_down, $conn); - push(@$conn_replace, $conn); - if ($peer ne '0.0.0.0') { - push(@$conn_up, $conn); - } + my ( $peer, $tunnel, $conn_down, $conn_replace, $conn_up ) = @_; + my $conn = "peer-$peer-tunnel-$tunnel"; + $conn =~ s/peer-@/peer-/; + push( @$conn_down, $conn ); + push( @$conn_replace, $conn ); + if ( $peer ne '0.0.0.0' ) { + push( @$conn_up, $conn ); + } } sub deleteConnection { - my ($conn, $conn_down, $conn_delete) = @_; - push(@$conn_down, $conn); - push(@$conn_delete, $conn); + my ( $conn, $conn_down, $conn_delete ) = @_; + push( @$conn_down, $conn ); + push( @$conn_delete, $conn ); } sub isFullRestartRequired { - my ($vcVPN) = @_; - - my $restartf = 0; - + my ($vcVPN) = @_; + + my $restartf = 0; + + # + # Check for configuration differences + # + # + # See what has been changed + # + if ( $vcVPN->isChangedOrDeleted('ipsec copy-tos') ) { + # - # Check for configuration differences + # Top level system parameter modified; full restart required # + + $restartf = 1; + } elsif ( $vcVPN->isChangedOrDeleted('ipsec logging') ) { + # - # See what has been changed + # Top level system parameter modified; full restart required # - if ($vcVPN->isChangedOrDeleted('ipsec copy-tos')) { - # - # Top level system parameter modified; full restart required - # - - $restartf = 1; - } elsif ($vcVPN->isChangedOrDeleted('ipsec logging')) { - # - # Top level system parameter modified; full restart required - # - - $restartf = 1; - } elsif ($vcVPN->isChangedOrDeleted('ipsec ipsec-interfaces')) { - # - # Top level system parameter modified; full restart required - # - - $restartf = 1; - } elsif ($vcVPN->isChangedOrDeleted('ipsec nat-traversal')) { - # - # Top level system parameter modified; full restart required - # - - $restartf = 1; - } elsif ($vcVPN->isChangedOrDeleted('ipsec nat-networks')) { - # - # Top level system parameter modified; full restart required - # - # FIXME: in reality this global doesn't affect every tunnel - - $restartf = 1; - } elsif (hasLocalWildcard($vcVPN, 0) != hasLocalWildcard($vcVPN, 1)) { - # local wild card has changed. this affects ipsec-interfaces. - $restartf = 1; - } - - return $restartf; + + $restartf = 1; + } elsif ( $vcVPN->isChangedOrDeleted('ipsec ipsec-interfaces') ) { + + # + # Top level system parameter modified; full restart required + # + + $restartf = 1; + } elsif ( $vcVPN->isChangedOrDeleted('ipsec nat-traversal') ) { + + # + # Top level system parameter modified; full restart required + # + + $restartf = 1; + } elsif ( $vcVPN->isChangedOrDeleted('ipsec nat-networks') ) { + + # + # Top level system parameter modified; full restart required + # + # FIXME: in reality this global doesn't affect every tunnel + + $restartf = 1; + } elsif ( hasLocalWildcard( $vcVPN, 0 ) != hasLocalWildcard( $vcVPN, 1 ) ) { + + # local wild card has changed. this affects ipsec-interfaces. + $restartf = 1; + } + + return $restartf; } sub dependenciesChanged { - my ($vcVPN, $peer, $tunnel) = @_; - my $auth_mode = $vcVPN->returnValue("ipsec site-to-site peer $peer authentication mode"); - if (defined($auth_mode) && $auth_mode eq 'rsa') { - if ($vcVPN->isChangedOrDeleted('rsa-keys local-key')) { - return 1; - } - my $rsa_key_name = $vcVPN->returnValue("ipsec site-to-site peer $peer authentication rsa-key-name"); - if ($vcVPN->isChangedOrDeleted("rsa-keys rsa-key-name $rsa_key_name")) { - return 1; - } + my ( $vcVPN, $peer, $tunnel ) = @_; + my $auth_mode = + $vcVPN->returnValue("ipsec site-to-site peer $peer authentication mode"); + if ( defined($auth_mode) && $auth_mode eq 'rsa' ) { + if ( $vcVPN->isChangedOrDeleted('rsa-keys local-key') ) { + return 1; } - - my $ike_group = $vcVPN->returnValue("ipsec site-to-site peer $peer ike-group"); - if ($vcVPN->isChangedOrDeleted("ipsec ike-group $ike_group")) { - return 1; + my $rsa_key_name = $vcVPN->returnValue( + "ipsec site-to-site peer $peer authentication rsa-key-name"); + if ( $vcVPN->isChangedOrDeleted("rsa-keys rsa-key-name $rsa_key_name") ) { + return 1; } + } - my $esp_group = $vcVPN->returnValue("ipsec site-to-site peer $peer tunnel $tunnel esp-group"); - if ($vcVPN->isChangedOrDeleted("ipsec esp-group $esp_group")) { - return 1; - } + my $ike_group = + $vcVPN->returnValue("ipsec site-to-site peer $peer ike-group"); + if ( $vcVPN->isChangedOrDeleted("ipsec ike-group $ike_group") ) { + return 1; + } + + my $esp_group = $vcVPN->returnValue( + "ipsec site-to-site peer $peer tunnel $tunnel esp-group"); + if ( $vcVPN->isChangedOrDeleted("ipsec esp-group $esp_group") ) { + return 1; + } - return 0; + return 0; } sub printTree { - my ($vc, $path, $depth) = @_; - - my @children = $vc->listNodes($path); - foreach my $child (@children) { - print ' ' x $depth; - print $child . "\n"; - printTree($vc, "$path $child", $depth + 1); - } + my ( $vc, $path, $depth ) = @_; + + my @children = $vc->listNodes($path); + foreach my $child (@children) { + print ' ' x $depth; + print $child . "\n"; + printTree( $vc, "$path $child", $depth + 1 ); + } } sub printTreeOrig { - my ($vc, $path, $depth) = @_; - - my @children = $vc->listOrigNodes($path); - foreach my $child (@children) { - print ' ' x $depth; - print $child . "\n"; - printTreeOrig($vc, "$path $child", $depth + 1); - } + my ( $vc, $path, $depth ) = @_; + + my @children = $vc->listOrigNodes($path); + foreach my $child (@children) { + print ' ' x $depth; + print $child . "\n"; + printTreeOrig( $vc, "$path $child", $depth + 1 ); + } } sub hasLocalWildcard { - my $vc = shift; - my $orig = shift; - my @peers = $vc->listNodes('ipsec site-to-site peer'); + my $vc = shift; + my $orig = shift; + my @peers = $vc->listNodes('ipsec site-to-site peer'); + if ($orig) { + @peers = $vc->listOrigNodes('ipsec site-to-site peer'); + } + return 0 if ( @peers == 0 ); + foreach my $peer (@peers) { + my $lip = $vcVPN->returnValue("ipsec site-to-site peer $peer local-ip"); if ($orig) { - @peers = $vc->listOrigNodes('ipsec site-to-site peer'); - } - return 0 if (@peers == 0); - foreach my $peer (@peers) { - my $lip - = $vcVPN->returnValue("ipsec site-to-site peer $peer local-ip"); - if ($orig) { - $lip = $vcVPN->returnOrigValue( - "ipsec site-to-site peer $peer local-ip"); - } - return 1 if ($lip && $lip eq '0.0.0.0'); + $lip = $vcVPN->returnOrigValue("ipsec site-to-site peer $peer local-ip"); } - return 0; + return 1 if ( $lip && $lip eq '0.0.0.0' ); + } + return 0; } # end of file |