#! /usr/bin/perl

use NetAddr::IP; # apt-get install libnetaddr-ip-perl

my $regs=supernetciscoacl2preg("147.210.253.0/23");

print "reg=$regs\n";

$mm= $m= " permit ip 148.210.0.0 0.0.255.255 any ";
$m=~ s/$regs/match/g;
print "$mm -> $m\n";

$mm= $m= " permit ip 147.210.0.0 0.0.255.255 any ";
$m=~ s/$regs/match/g;
print "$mm -> $m\n";

$mm= $m= " permit ip 147.210.0.0 0.0.0.255 any ";
$m=~ s/$regs/match/g;
print "$mm -> $m\n";

$mm= $m= " permit ip 147.210.253.0 0.0.0.255 any ";
$m=~ s/$regs/match/g;
print "$mm -> $m\n";

$mm= $m= " permit ip 147.210.252.0 0.0.1.255 any ";
$m=~ s/$regs/match/g;
print "$mm -> $m\n";

# @TODO@ should write a supernet2preg whith a first parameter which could take the type
# of format we want for the output matching : 
# if we want to match : format                 : sample 
# cisco-acl           : ip inverted-dot-mask    147.210.0.0 0.0.255.255 + notations : 0.0.0.0 0.0.0.0 = any + host x.x.x.x
# cisco-route         : ip dot-mask             147.210.0.0 255.255.0.0
# cidr                : ip/bits                 147.210.0.0/16
# ...


sub supernetciscoacl2preg
{
    ## build a regexp which match nets that inlude the given subnet in the form A.B.C.D/n
    ## => build regexp that match "bigger" prefixes /n /n-1 /n-2 ... /0
    
    ## can be easily modified to match any form of subnet (adresse/mask, adress mask, adress invertedmask, address/bits...)
    ## 02/2005 - Laurent FACQ - facq@u-bordeaux.fr - LGPL

    ## possibles problems when matchin acl like (have to think about it later) 
    ## permit tcp A.B.C.D E.F.G.H I.J.K.L M.N.O.P
    ## may be in some case, we could badly match E.F.G.H I.J.K.L 
  

    my ($net)= @_;
    my $ip= new NetAddr::IP($net);
    my ($subnet,$bits)= ($ip->addr(), $ip->masklen());
    my ($b,$res);

    my @reg= ('any'); # always match !

    if ($bits eq 32)
    {
	push @reg, '\\shost\\s+'.$subnet;
    }

    foreach $b ( 0..($bits) )  ### replace $bits by ((($bits-1)>0)?($bits-1):0) for strict inclusion
    {
        $res= applymaskprefix("$subnet",$b).'\s+'.invertmask(applymaskprefix("255.255.255.255",$b));
	push @reg, $res;
    }

    $res= "(?:(?<![0-9])(?:".join("|",@reg).")(?![0-9]))";### FIXME [0-9] is not right, \s ? LF
    $res =~ s/\./\\./g;


    return $res;
}

sub invertmask
{
    my ($mask)= @_;    
    return join('.',map 255-$_,split('\.',$mask));
}

sub applymaskprefix   # ("147.210.253.1",16) -> "147.210.0.0")
{
    my ($snet,$prefix)= @_;
    my (@ip,@msk,$i);

    ### dirty but... easy  :) 
    my (@maskprefix)= ('0.0.0.0','128.0.0.0','192.0.0.0','224.0.0.0','240.0.0.0','248.0.0.0','252.0.0.0','254.0.0.0','255.0.0.0','255.128.0.0','255.192.0.0','255.224.0.0','255.240.0.0','255.248.0.0','255.252.0.0','255.254.0.0','255.255.0.0','255.255.128.0','255.255.192.0','255.255.224.0','255.255.240.0','255.255.248.0','255.255.252.0','255.255.254.0','255.255.255.0','255.255.255.128','255.255.255.192','255.255.255.224','255.255.255.240','255.255.255.248','255.255.255.252','255.255.255.254','255.255.255.255');

    if ($prefix == 32)
    {
	return $snet;
    }
    
    @ip= split('\.',$snet);

    @msk= split('\.',$maskprefix[$prefix]);
    
    # print "prefix $prefix : mask ".join('.',@msk)." ip : ".join('.',@ip)."\n";

    foreach $i ( 0..3 )
    {
	$ip[$i]= ($ip[$i]+0) & ($msk[$i]+0);
    }

    return join('.',@ip);
}
