]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
swanctl: Add a perl script to convert ipsec.conf to swanctl.conf ipsec2swanctl
authorMartin Willi <martin@revosec.ch>
Tue, 3 Jun 2014 07:22:35 +0000 (09:22 +0200)
committerMartin Willi <martin@revosec.ch>
Tue, 3 Jun 2014 07:23:52 +0000 (09:23 +0200)
src/swanctl/Makefile.am
src/swanctl/ipsec2swanctl.pl [new file with mode: 0644]

index a262a2fa7b44082886c61cfcd7c978f5a5c0b9bc..3d86fb712329fb411693094521936b33c22e5964 100644 (file)
@@ -34,7 +34,7 @@ man_MANS = \
        swanctl.conf.5
 
 BUILT_SOURCES = swanctl.conf swanctl.conf.5.main
-EXTRA_DIST = swanctl.opt swanctl.conf swanctl.conf.5.main
+EXTRA_DIST = swanctl.opt swanctl.conf swanctl.conf.5.main ipsec2swanctl.pl
 CLEANFILES = $(man_MANS)
 
 .opt.conf:
diff --git a/src/swanctl/ipsec2swanctl.pl b/src/swanctl/ipsec2swanctl.pl
new file mode 100644 (file)
index 0000000..b9b9672
--- /dev/null
@@ -0,0 +1,849 @@
+#!/usr/bin/perl -w
+
+my $etc = "/etc";
+my $debug = 3;
+my $tab = "\t";
+my $min_connection_name = 4;
+
+my %debug_level =
+(
+   'MIN'            => 0,
+   'ERROR'          => 1,
+   'WARN'           => 2,
+   'INFO'           => 3,
+   'DEBUG'          => 4,
+);
+
+my %any_ike_sa =
+(
+    'keyexchange'   => 'version',
+    'left'          => 'local_addrs',
+    'right'         => 'remote_addrs',
+    'leftikeport'   => 'local_port',
+    'rightikeport'  => 'remote_port',
+    'ike'           => 'proposals',
+    'leftsourceip'  => 'vips',
+    'aggressive'    => 'aggressive',
+    'modeconfig'    => 'pull',
+    'forceencaps'   => 'encap',
+    'mobike'        => 'mobike',
+    'dpddelay'      => 'dpd_delay',
+    'dpdtimeout'    => 'dpd_timeout',
+    'fragmentation' => 'fragmentation',
+    'rightsendcert' => 'send_certreq',
+    'leftsendcert'  => 'send_cert',
+    'keyingtries'   => 'keyingtries',
+    'uniqueids'     => 'unique',
+    'ikelifetime'   => 'reauth_time',
+#    'ikelifetime'   => 'rekey_time',
+    'rekeymargin'   => 'over_time',
+    'rekeyfuzz'     => 'rand_time',
+);
+
+my %child_sa =
+(
+    'ah'            => 'ah_proposals',
+    'esp'           => 'esp_proposals',
+    'leftsubnet'    => 'local_ts',
+    'rightsubnet'   => 'remote_ts',
+    'leftprotoport' => 'local_ts',
+    'rightprotoport'=> 'remote_ts',
+    'leftupdown'    => 'update',
+    'lefthostaccess'=> 'hostaccess',
+    'type'          => 'mode',
+    'dpdaction'     => 'dpd_action',
+    'compress'      => 'ipcomp',
+    'inactivity'    => 'inactivity',
+    'reqid'         => 'reqid',
+    'mark'          => 'mark_in',
+    'mark_in'       => 'mark_in',
+    'mark_out'      => 'mark_out',
+    'tfc'           => 'tfc_padding',
+    'margintime'    => 'rekey_time',
+    'lifetime'      => 'life_time',
+    'rekeyfuzz'     => 'rand_time',
+    'marginpackets' => 'rekey_packets',
+    'lifepackets'   => 'life_packets',
+    'rekeyfuzz'     => 'rand_packets',
+    'marginbytes'   => 'rekey_bytes',
+    'lifebytes'     => 'life_bytes',
+    'auto'          => 'start_action',
+);
+
+my %local =
+(
+    'leftcert'      => 'certs',
+    'leftca'        => 'cacerts',
+    'leftcacert'    => 'cacerts',
+    'leftauth'      => 'auth',
+    'leftid'        => 'id',
+    'eap_identity'  => 'eap_id',
+    'aaa_identity'  => 'aaa_id',
+    'xauth_identity'=> 'xauth_id',
+    'rightgroups'   => 'groups',
+    'strictcrlpolicy'=> 'revocation',
+);
+
+my %remote =
+(
+    'rightcert'     => 'certs',
+    'rightca'       => 'cacerts',
+    'rightcacert'   => 'cacerts',
+    'rightauth'     => 'auth',
+    'rightid'       => 'id',
+    'eap_identity'  => 'eap_id',
+    'aaa_identity'  => 'aaa_id',
+    'xauth_identity'=> 'xauth_id',
+    'rightgroups'   => 'groups',
+    'strictcrlpolicy'=> 'revocation',
+);
+
+my %cert_dirs =
+(
+    'certs'         => 'x509',
+    'cacerts'       => 'x509ca',
+    'aacerts'       => 'x509aa',
+    'crls'          => 'x509crl',
+    'acerts'        => 'x509ac',
+);
+
+my %ipsec_rename =
+(
+    'ipsec.d'       => 'swanctl',
+    'ipsec.conf'    => 'swanctl.conf',
+#    'ipsec.secrets' => 'swanctl.conf',
+);
+
+sub cmd
+{
+   my $shell_cmd = $_[0];
+
+   my $shell_out = `$shell_cmd`;
+
+   if ( $? != 0 )
+     {
+     if ( $debug > $debug_level{ 'WARN' } )
+       {
+       print "WARN: EXEC $shell_cmd\n";
+       print "WARN: cmd returned with code $?";
+       }
+     }
+   elsif ( $debug > $debug_level{ 'DEBUG' } )
+       {
+       print "DEBUG: EXEC: $shell_cmd\n";
+       }
+
+   return $shell_out;
+}
+
+sub copy_ipsec_d
+{
+   my $ipsec_dir = "$etc/ipsec.d";
+
+   if ( $debug > $debug_level{ 'INFO' } )
+     { print "INFO: creating $etc/swanctl from $ipsec_dir\n"; }
+
+   if ( ! -d $ipsec_dir ) { die ( "Cannot locate $ipsec_dir cert directory !" ); }
+
+   &cmd( "mkdir -p $etc/swanctl" );
+
+   &cmd( "cp -pR $etc/ipsec.d/* $etc/swanctl/" );
+
+   foreach my $dir ( keys %cert_dirs )
+      {
+      &cmd( "mv $etc/swanctl/$dir $etc/swanctl/$cert_dirs{ $dir }" );
+      }
+
+   &cmd( "mkdir $etc/swanctl/rsa" );
+   &cmd( "mkdir $etc/swanctl/ecdsa" );
+   &cmd( "mkdir $etc/swanctl/pkcs8" );
+
+   &cmd( "mv -v $etc/swanctl/private/*.der $etc/swanctl/ecdsa" );
+   &cmd( "mv -v $etc/swanctl/private/*.rsa $etc/swanctl/rsa" );
+   &cmd( "mv -v $etc/swanctl/private/* $etc/swanctl/pkcs8" );
+
+   &cmd( "rmdir $etc/swanctl/private" );
+}
+
+my @connections = ();
+
+my %conn_values = ();
+
+my %conn_order = ();
+
+my %conn_remark = ();
+
+my %conn_deps = ();
+
+my %leftright_groups = ();
+
+my %also_list = ();
+
+my @ipsec_conf = ();
+
+sub any_ike_sa_count
+{
+  my ( $val ) = ( @_ );
+  my $count = 0;
+
+  foreach my $k ( keys %$val )
+    {
+    if ( defined( $any_ike_sa{ $k } ) )
+      { $count++; }
+    }
+  return $count;
+}
+
+sub get_leftright_key
+{
+  my ( $vals ) = ( @_ );
+
+  if ( defined $$vals{ 'left' } && defined $$vals{ 'right' } )
+    {
+    return $$vals{ 'left' }.'-'.$$vals{ 'right' };
+    }
+  return undef
+}
+
+sub push_leftright_group
+{
+  my ( $conn_name, $left, $right, $vals ) = ( @_ );
+
+  $leftright = "$left-$right";
+  my $group = [];
+
+  if ( defined( $leftright_groups{ $leftright } ) )
+    {
+    $group = $leftright_groups{ $leftright };
+    }
+
+  push @$group, $conn_name;
+
+  $leftright_groups{ $leftright } = $group;
+}
+
+sub parse_ipsec_conf
+{
+  my $conn_name = undef;
+  my $comments = [];
+  my $vals = {};
+  my $order = [];
+  my $also = [];
+  my $any_ike_sa_count = 0;
+  my $left = undef;
+  my $right = undef;
+  my $ipsec_file = "$etc/ipsec.conf";
+
+  if ( $debug > $debug_level{ 'INFO' } )
+    { print "INFO: parsing $ipsec_file\n"; }
+
+  open( IPSEC_FILE, "< $ipsec_file" ) || die "Could not open $ipsec_file: $!\n";
+  @ipsec_conf = <IPSEC_FILE>;
+
+  close( IPSEC_FILE );
+
+   foreach my $l ( 0..$#ipsec_conf )
+     {
+     my $line = $ipsec_conf[ $l ];
+
+     if ( $line =~ /^\s*conn\s+(.+)/ ||
+          $line =~ /^\s*config\s+(.+)/ )
+       {
+       if ( defined ( $conn_name ) )
+         {
+         push @connections, $conn_name;
+         $conn_values{ $conn_name } = $vals;
+         $conn_order{ $conn_name } = $order;
+
+         if ( defined( $left ) && defined( $right ) )
+           {
+           &push_leftright_group( $conn_name, $left, $right, $vals );
+           undef $left;
+           undef $right;
+           }
+
+         if ( 0 < scalar( @$also ) )
+           {
+           $conn_deps{ $conn_name } = $also;
+           $also = [];
+           }
+
+         $vals = {};
+         $order = [];
+         }
+
+       $conn_name = $1;
+
+       if ( @$comments )
+          {
+          $conn_remark{ $conn_name } = $comments;
+          $comments = [];
+          }
+       }
+     elsif ( $line =~ /^\s*(#.+)/ )
+       {
+       push @$comments, $1;
+       }
+     elsif ( $line =~ /^\s*$/ )
+       {
+       push @$comments, "";
+       }
+     elsif ( $line =~ /^\s*(\w+)\s*=\s*(.+)$/ )
+       {
+       if ( @$comments )
+         {
+         push @$order, @$comments;
+         $comments = [];
+         }
+       my $key = $1;
+       my $val = $2; # note this may include value and comments !
+       my $value = $val;
+
+       # sanitize remarks out of value for post-processing:
+       if ( $val =~ /^(\S+)\s*#.*/ )
+         { $value = $1; }
+
+       if ( $key eq 'also' )
+         {
+         $also_list{ $value }++;
+         push @$also, $value;
+         next;
+         }
+       elsif ( $key eq "right" )
+         { $right = $value; }
+       elsif ( $key eq "left" )
+         { $left = $value; }
+
+       push @$order, $key;
+       $$vals{ $key } = $val;
+      }
+   }
+
+  if ( defined ( $conn_name ) )
+    {
+    push @connections, $conn_name;
+    $conn_values{ $conn_name } = $vals;
+    $conn_order{ $conn_name } = $order;
+
+    if ( defined( $left ) && defined( $right ) )
+      { &push_leftright_group( $conn_name, $left, $right, $vals ); }
+    }
+}
+
+sub dump_ipsec_conf
+{
+  foreach my $conn ( @connections )
+    {
+    if ( defined $conn_remark{ $conn } )
+      {
+      my $remark = $conn_remark{ $conn };
+
+      foreach my $l ( @$remark )
+        {
+        print $l."\n";
+        }
+      }
+
+    print "conn $conn\n";
+    my $order = $conn_order{ $conn };
+    my $vals  = $conn_values{ $conn };
+
+    foreach my $l ( @$order )
+      {
+      if ( $l eq "" || '#' eq substr( $l, 0, 1 ) )
+        {
+        print $l."\n";
+        }
+      else
+        {
+        print $l."=".$$vals{ $l }."\n";
+        }
+      }
+    }
+}
+
+sub process_dependencies
+{
+  my @new_conn = ();
+
+  my $default = undef;
+
+  if ( defined( $conn_order{ '%default' } ) )
+    { $default = $conn_order{ '%default' }; }
+
+  foreach my $conn ( @connections )
+    {
+    my $exclude = 0;
+
+    # exclude the "setup" and "conn %default" from getting converted:
+    if ( $conn eq "setup" ||
+         $conn eq "%default" )
+      {
+      if ( $debug > $debug_level{ 'DEBUG' } )
+        { print "DEBUG: excluding conn $conn\n"; }
+      $exclude = 1;
+      }
+    # Check if any "also" referenced conn are valid
+    elsif ( defined( $also_list{ $conn } ) )
+      {
+      $exclude = 1;
+
+      my $vals  = $conn_values{ $conn };
+
+      # Look for a "left" and "right" keyword
+      if ( defined( &get_leftright_key( $vals ) ) )
+        {
+        $exclude = 0;
+        }
+      }
+    # check if a conn has "also" references, add the "also" ordered
+    # set of values to conn's ordered set of values
+    else
+      {
+      if ( defined( $conn_order{ $conn } ) )
+        {
+        my $order = [];
+        push @$order, @{$conn_order{ $conn }};
+
+        if ( defined $conn_deps{ $conn } )
+          {
+          foreach my $also ( @{ $conn_deps{ $conn } } )
+            {
+            if ( defined( $conn_order{ $also } ) )
+              {
+              push @$order, @{ $conn_order{ $also } };
+              }
+            }
+          }
+
+        if ( defined( $default ) )
+          { push @$order, @{ $default }; }
+
+        $conn_order{ $conn } = $order;
+
+        if ( $debug > $debug_level{ 'DEBUG' } )
+          {
+          print "DEBUG: new_order $conn\n";
+          foreach my $o( @$order )
+            { print "DEBUG: $o\n"; }
+          }
+        }
+      }
+
+    if ( ! $exclude )
+        { push @new_conn, $conn; }
+    elsif ( $debug > $debug_level{ 'DEBUG' } )
+        { print "DEBUG: excluding conn $conn\n"; }
+    }
+
+  @connections = @new_conn;
+}
+
+sub get_conn_values
+{
+  my ( $conn_name, $default ) = ( @_ );
+  my %conn_union_vals = %$default;
+  my $conn_vals = $conn_values{ $conn_name };
+
+  # Overlay also dependant values
+  if ( defined $conn_deps{ $conn_name } )
+    {
+    foreach my $also ( @{ $conn_deps{ $conn_name } } )
+      {
+      my $also_vals = &get_conn_values( $also, $default );
+      @conn_union_vals{ keys %$also_vals } = values %$also_vals;
+      }
+    }
+
+  # Overlay the specific conn values
+  @conn_union_vals{ keys %$conn_vals } = values %$conn_vals;
+
+  # add id:
+  $conn_union_vals{ 'CONNECTION_NAME' } = $conn_name;
+
+  # fix some obsolete variable values
+  foreach my $direction ( 'left', 'right' )
+    {
+    if ( defined( $conn_union_vals{ $direction.'protoport' } ) &&
+         defined( $conn_union_vals{ $direction } ) )
+      {
+
+      if ( defined( $conn_union_vals{ $direction.'subnet' } ) )
+        {
+        print "ERROR: cannot convert ".$direction.'protoport because  '.$direction.'subnet already exists\n';
+        }
+
+      my $val = $conn_union_vals{ $direction.'protoport' };
+      my $host = $conn_union_vals{ $direction };
+      my $remark = "";
+
+      # first sanitize for comment
+      if ( $val =~ /^([^#]+)(#.*)/ )
+        {
+        $remark = $2;
+        my $val_space = $1;
+
+        if ( $val_space =~ /^(\S+)(\s*)/ )
+          {
+          $remark = $2.$remark;
+          $val = $1;
+          }
+        }
+      # set {left,right}protoport equal to the {left,right}subnet format
+      $conn_union_vals{ $direction.'protoport' } = $host.'['.$val.']'.$remark;
+      }
+   }
+
+ if ( $debug > $debug_level{ 'DEBUG' } )
+    {
+    print "DEBUG: GET_CONN_VALUES ".$conn_union_vals{ 'CONNECTION_NAME' }."\n";
+
+    foreach my $k ( keys %conn_union_vals )
+      {
+      print "DEBUG: ".$k." = ".$conn_union_vals{ $k }."\n";
+      }
+  }
+
+  return \%conn_union_vals;
+}
+
+sub output_section
+{
+  my ( $SWANCTL, $conn_vals, $section_keys, $indent, $info, $conn_child ) = ( @_ );
+
+  my $conn_name = $$conn_vals{ 'CONNECTION_NAME' };
+  my $order = $conn_order{ $conn_name };
+  if ( $debug > $debug_level{ 'DEBUG' } )
+    { print "DEBUG: output_section $conn_name\n"; }
+  for( my $o = 0; $o < $#$order; $o++ )
+    {
+    my $oldkey = $$order[ $o ];
+    if ( $debug > $debug_level{ 'DEBUG' } )
+      { print "DEBUG: $conn_name $oldkey\n"; }
+    next if ( ! defined( $$conn_vals{ $oldkey } ) );
+    next if ( ! defined( $$section_keys{ $oldkey } ) );
+    my $newkey = $$section_keys{ $oldkey };
+
+    if ( $info ne "" )
+      {
+      print $SWANCTL $indent.$info."\n";
+      # only output this info section once
+      $info = "";
+      }
+    # check if there were comments associated with this key/value:
+    my $p = $o;
+    $o--;
+    while( $o >= 0 && substr( $$order[ $o ], 0, 1 ) eq "#" )
+      { $o--; }
+    $o++;
+    for(; $o < $p; $o++ )
+      {
+      print $SWANCTL $indent.$$order[ $o ]."\n";
+      }
+
+    # write out the $newkey = $value.
+    my $value = $$conn_vals{ $oldkey };
+    delete $$conn_vals{ $oldkey };
+    print $SWANCTL $indent.$newkey." = ".$value."\n";
+
+    next if ( ! defined( $conn_child ) || ref( $conn_child ) ne 'ARRAY' );
+
+    # loop through all the children. For any values *not* the same leave a WARN comment
+    foreach my $child_vals ( @$conn_child )
+      {
+      next if ( ! defined ( $$child_vals{ $oldkey } ) );
+      my $child_val = $$child_vals{ $oldkey };
+      delete $$child_vals{ $oldkey };
+
+      # sanitize remarks out for comparison
+      if ( $value =~ /^(\S+)\s*#.*/ )
+        { $value = $1; }
+      if ( $child_val =~ /^(\S+)\s*#.*/ )
+        { $child_val = $1; }
+
+      if ( $debug > $debug_level{ 'DEBUG' } )
+        { print "DEBUG: $conn_name compare $value eq $child_val\n"; }
+
+      if ( $child_val ne $value )
+        {
+        print $SWANCTL $indent."# WARN ".$$child_vals{ 'CONNECTION_NAME' }." ".$newkey." = ".$value."\n";
+        }
+      }
+   }
+
+  # loop through all the children. For any values that weren't in the
+  # main conn add them with an INFO comment
+  foreach my $child_vals ( @$conn_child )
+    {
+    &output_section( $SWANCTL, $child_vals, $section_keys, $indent, "# INFO: from conn ".$$child_vals{ 'CONNECTION_NAME' } );
+    }
+
+  return;
+}
+
+sub output_block
+{
+  my ( $SWANCTL, $conn_vals, $section_keys, $indent, $block, $conn_child ) = ( @_ );
+
+  if ( $debug > $debug_level{ 'DEBUG' } )
+    { print "DEBUG: output_block $block\n"; }
+
+  print $SWANCTL "\n";
+  print $SWANCTL $indent.$block." {\n";
+
+  &output_section( $SWANCTL, $conn_vals, $section_keys, $indent.$tab, "", $conn_child );
+
+  print $SWANCTL $indent."}\n";
+}
+
+sub output_swanctl_conf
+{
+  my $swanctl_file = "$etc/swanctl/swanctl.conf";
+
+  if ( $debug > $debug_level{ 'INFO' } )
+    { print "INFO: output $swanctl_file\n"; }
+
+  open( my $SWANCTL, "> $swanctl_file" ) || die ( "ERROR: could not open $swanctl_file: $!\n" );
+
+  my @indent = ( "" );
+  print $SWANCTL $indent[0]."connections {\n";
+
+  my $default = {};
+
+  if ( defined( $conn_values{ '%default' } ) )
+    { $default = $conn_values{ '%default' }; }
+
+  # We want to quasi follow the ordered structure of the original ipsec.conf,
+  # in that we will write the swanctl.conf connections based on $leftright pairs
+  # as they appeared in the ipsec.conf
+  foreach my $conn ( @connections )
+    {
+    my @name_chars = split( //, $conn );
+    my $name_len   = $#name_chars;
+    my $conn_vals  = &get_conn_values( $conn, $default );
+    my @conn_child = ();
+
+    my $leftright = &get_leftright_key( $conn_vals );
+
+    next if ( ! defined( $leftright ) );
+    next if ( ! defined( $leftright_groups{ $leftright } ) );
+
+    my $conn_count = &any_ike_sa_count( $conn_vals );
+
+    # figure out which of the connections that share the same $leftright key
+    # should be considered the master because it has the most values:
+    foreach my $child ( @{ $leftright_groups{ $leftright } } )
+      {
+      next if ( $child eq $conn );
+
+      my $child_vals  = &get_conn_values( $child, $default );
+      my $child_count = &any_ike_sa_count( $child_vals );
+
+      # Figure out the name for the connection - hopefully there is commonality !
+      my @child_name = split( //, $child );
+      my $name_len   = $name_len > $#child_name ? $#child_name : $name_len;
+      foreach my $i ( 0 .. $name_len )
+        {
+        if ( $name_chars[ $i ] ne $child_name[ $i ] )
+          {
+          $name_len = $i;
+          last;
+          }
+        }
+
+      if ( $child_count > $conn_count )
+        {
+        push @conn_child, $conn_vals;
+        $conn_vals  = $child_vals;
+        $conn_count = $child_count;
+        $conn       = $child;
+        @name_chars = @child_name;
+        }
+      else
+        {
+        push @conn_child, $child_vals;
+        }
+      }
+
+    # at this point, we should have the "master" to use for the $leftright conn
+    delete $leftright_groups{ $leftright };
+
+    # name to use: check min, length, sanitize any "_-" ending chars
+    my $conn_name = $conn;
+
+    foreach my $char ( [ '-', '_' ] )
+      {
+      my $len = index( $conn_name, '-' );
+      if ( $len >= $min_connection_name && $len < $name_len )
+        { $name_len = $len; }
+      }
+
+    if ( $name_len >= $min_connection_name )
+      { $conn_name = substr( $conn, 0, $name_len ); }
+
+    if ( $debug > $debug_level{ 'DEBUG' } )
+      { print "DEBUG: connection name = $conn_name\n"; }
+
+    # Done processing. Now write to swanctl.conf
+    unshift @indent, $indent[0].$tab;
+
+    if ( defined $conn_remark{ $conn } )
+      {
+      my $remark = $conn_remark{ $conn };
+
+      foreach my $l ( @$remark )
+        {
+        print $SWANCTL $indent[0].$l."\n";
+        }
+      }
+
+    # conn block
+    print $SWANCTL $indent[0].$conn_name." {\n";
+    unshift @indent, $indent[0].$tab;
+
+    &output_section( $SWANCTL, $conn_vals, \%any_ike_sa, $indent[0], "", \@conn_child );
+
+    # local block
+    &output_block( $SWANCTL, $conn_vals, \%local, $indent[0], "local", \@conn_child );
+
+    # remote block
+    &output_block( $SWANCTL, $conn_vals, \%remote, $indent[0], "remote", \@conn_child );
+
+    # add the master $conn_vals to $conn_child so simple iteration
+    # for remaining of output
+    unshift @conn_child, $conn_vals;
+
+    # children block
+    print $SWANCTL "\n";
+    print $SWANCTL $indent[0]."children {";
+    unshift @indent, $indent[0].$tab;
+    foreach my $children ( @conn_child )
+      {
+      &output_block( $SWANCTL, $children, \%child_sa, $indent[0], $$children{ 'CONNECTION_NAME' } );
+      }
+
+    shift @indent;
+    print $SWANCTL $indent[0]."}\n";
+
+    # WARN about any remaining variables that haven't been mapped
+    unshift @indent, $indent[0]."# ";
+    foreach my $remain ( @conn_child )
+      {
+      my %remain_keys = map{ $_ => $_ } keys %$remain;
+
+      &output_section( $SWANCTL, $remain, \%remain_keys, $indent[0], "WARN: nomap ".$$remain{ 'CONNECTION_NAME' } );
+      }
+    # remove just add "# "
+    shift @indent;
+
+    shift @indent;
+    print $SWANCTL $indent[0]."}\n";
+    shift @indent;
+    }
+
+  print $SWANCTL $indent[0]."} # connections\n";
+
+  my $secrets_file = "$etc/ipsec.secrets";
+
+  if ( -f $secrets_file )
+    {
+    if ( $debug > $debug_level{ 'INFO' } )
+      { print "INFO: processing $secrets_file\n"; }
+
+    print $SWANCTL "\n";
+    print $SWANCTL "secrets {\n";
+    print $SWANCTL $tab."eap {\n";
+    print $SWANCTL $tab."}\n";
+    print $SWANCTL $tab."ike {\n";
+    print $SWANCTL $tab."}\n";
+    print $SWANCTL "} # secrets\n";
+    }
+
+  close( $SWANCTL );
+}
+
+sub print_usage
+{
+  print <<END;
+USAGE: ipsec2swanctl.pl - converts a strongswan pre-5.2.0 configuration to
+USAGE:   the new format. Takes the path the configuration directory, usually
+USAGE:   /etc, converts the ipsec.conf, ipsec.secrets to swanctl.conf,
+USAGE:   and creates a swanctl directory from the ipsec.d cert directory.
+USAGE:   All the original configuration and directories are read-only.
+USAGE:
+USAGE: ./ipsec2swanctl.pl [etc_dir] [debug_level] [tab] [min_conn_name_len]
+USAGE:   [etc_dir] - optional directory containing pre-5.2.0 configuration
+USAGE:               defaults to /etc
+USAGE:   [debug_level] - optional debug level of ERROR, WARN, INFO, DEBUG.
+USAGE:                   defaults to WARN.
+USAGE:   [tab] - optional tab character, defaults to tab "\t" charactor
+USAGE:   [min_conn_name_len] - when attempting to calculate simplified
+USAGE:     connection name, minimum length of new name. If less than that
+USAGE:
+USAGE: For example:
+USAGE: ./ipsec2swanctl.pl ./etc DEBUG "   " 10
+
+END
+   exit(1);
+}
+
+sub parse_cmd_line
+{
+  if ( defined( $ARGV[ 0 ] ) )
+     {
+     $etc = $ARGV[ 0 ];
+     }
+
+  if ( $etc =~ /^-*he?l?p?/i )
+    {
+    &print_usage();
+    }
+  elsif ( ! -d "$etc" )
+    {
+    print "ERROR: $etc is not valid\n";
+    &print_usage();
+    }
+
+  if ( defined( $ARGV[ 1 ] ) )
+     {
+     my $level = $ARGV[ 1 ];
+     if ( ! defined( $debug_level{ $level } ) )
+       {
+       print "ERROR: valid debug values are ERROR, WARN, INFO, DEBUG\n";
+       &print_usage();
+       }
+     $debug = $debug_level{ $level };
+     # Require at least WARN level:
+     if ( $debug < $debug_level{ 'WARN' } )
+        { $debug = $debug_level{ 'WARN' }; }
+     }
+  $debug++;
+
+  if ( defined( $ARGV[ 2 ] ) )
+     {
+     my $tab = $ARGV[ 2 ];
+     }
+
+  if ( defined( $ARGV[ 3 ] ) )
+     {
+     my $min_connection_name = $ARGV[ 3 ];
+     }
+}
+
+sub main
+{
+  &parse_cmd_line();
+
+  &copy_ipsec_d();
+
+  &parse_ipsec_conf();
+
+  if ( $debug > $debug_level{ 'DEBUG' } )
+    { &dump_ipsec_conf(); }
+
+  &process_dependencies();
+
+  &output_swanctl_conf();
+}
+
+&main();