#! /usr/bin/perl -wall

my @config = ();  # store the lines of the config here

my %prefix_lists = ();
my %junos_policy = ();

my $cli = 0;

if ((defined($ARGV[0])) and ($ARGV[0] eq '-c'))
{
    shift @ARGV;
    $cli = 1;
}

# read the config into the @config list
my $line;
while (defined($line = <>)) 
{
    chomp $line;
    push @config, $line;
}

# parse the config looking for prefix-lists
my $linepos = 0;

while ($linepos < scalar(@config)) 
{
    
  STATEMENT: 
    {
	
	# skip comments
	if ($config[$linepos] =~ m/^!/) 
	{
	    $linepos++;
	    last STATEMENT;
	}

	# deal with prefix-lists
	if ($config[$linepos] =~ m/ip prefix-list (\S+) (.*)/)
	{
	    my $list_name = $1;
	    my $item_details = $2;
	    my @list_details = ();
	    
	    push @list_details, $item_details;

	    while ((++$linepos < scalar(@config)) && ($config[$linepos] =~ m/ip prefix-list $list_name (.*)/)) 
	    {
		push @list_details, $1;
	    }
		
	    $prefix_lists{$list_name} = \@list_details;
	    
	    last STATEMENT;
	}

	$linepos++;

    }
}

# Convert the IOS prefix lists into JUNOS policy statements 

my $s1 = '    ';
my $s2 = '        ';
my $s3 = '            ';
my $s4 = '                ';

# Boiler plate for JUNOS prefix_list

printf "policy-options {\n" if (!$cli);
printf "edit policy-options\n" if ($cli);

foreach $list_name (sort keys %prefix_lists) 
{
    @list_details = @{$prefix_lists{$list_name}}; 
    

    # Boiler plate for JUNOS prefix_list
    printf "%spolicy-statement %s {\n", $s1, $list_name if (!$cli);
    printf "%sterm accept {\n", $s2 if (!$cli);
    printf "%sfrom {\n", $s3 if (!$cli);

    my $cli_prefix = sprintf "set policy-statement %s", $list_name;
    
    foreach $item_details (sort item_sort @list_details)
    {
	# parse the prefix-list options and convert into JUNOS syntax

	if ($item_details =~ m/description (.*)/) {
	    printf "%s# %s\n", $s4, $1 if (!$cli);
	    next; 
	}

	next if ($item_details =~ m/deny/); # ignore deny items because we have an explict reject at the end

	if ($item_details =~ m/permit (seq \d+ )?(.*)/)
	{
	    my $good_bit = $2;
	    
	    if ($good_bit =~ m/(.*) le (.*)/) {
		printf "%sroute-filter %s upto /%s;\n", $s4, $1, $2 if (!$cli);
		printf "%s term accept from route-filter %s upto /%s\n", $cli_prefix, $1, $2 if ($cli);
		next;
	    } 

	    if ($good_bit =~ m/(.*) ge (.*)/) {
		printf STDERR "URRRG, can't deal with ge yet => %s\n", $item_details;
		next;
	    }

	    printf "%sroute-filter %s exact;\n", $s4, $good_bit if (!$cli);
	    printf "%s term accept from route-filter %s exact\n", $cli_prefix, $good_bit if ($cli);
	    next;

	}
	
	printf STDERR "URRRG, can't deal with => %s\n", $item_details;
		
    }

    # Boiler plate for JUNOS prefix_list
    printf "%s}\n", $s3 if (!$cli);
    printf "%sthen accept;\n", $s3 if (!$cli);
    printf "%s}\n", $s2 if (!$cli);

    printf "%sterm reject {\n", $s2 if (!$cli);
    printf "%sthen reject;\n", $s3 if (!$cli);
    printf "%s}\n", $s2 if (!$cli);
    printf "%s}\n", $s1 if (!$cli);

    printf "%s term accept then accept\n", $cli_prefix if ($cli);
    printf "%s term reject then reject\n", $cli_prefix if ($cli);

}

# Boiler plate for JUNOS prefix_list
printf "}\n" if (!$cli);

# Useful functions

sub item_sort
{

    my $a_ip = undef;
    my $b_ip = undef;

    $a_ip = $1 if ($a =~ m/(\d+\.\d+\.\d+\.\d+)/);
    $b_ip = $1 if ($b =~ m/(\d+\.\d+\.\d+\.\d+)/);
    
    return ip_to_int($a_ip) <=> ip_to_int($b_ip) if (defined $a_ip && defined $b_ip);
    return 1 if (defined $a_ip && not defined $b_ip);
    return -1 if (not defined $a_ip && defined $b_ip);
    return $a cmp $b if (not defined $a_ip && not defined $b_ip);

}

sub ip_to_int 
{

    my $ip = shift;
    my ($a, $b, $c, $d) = split(/\./, $ip);
    my $ipint = $d + ($c<<8) + ($b<<16) + ($a<<24);

    return $ipint;
}
