#!/usr/bin/perl
use strict;
my %classes;
my %users;
my $maxu=20000;
my $l=0;
my %opened;
my %plans;
my $allcfg=1;
$allcfg=0 if($ARGV[0] eq '-t');
while(<STDIN>){
    $l++;
    chomp;
    #next if(/^\s*#/);
    s/#.+$//g;
    s/^\s+//g;
    next if(/^\s*$/);
    if(/^class\s+(\d+)\D/){ #class definition
	my $class=$1;
	if ($classes{$class}){
	    die "Class $class defined at line $l and ".$classes{$class}->{l}."\n";
	}
	my $bw=undef;
	my $ubw=undef;
	my $name=undef;
	if(/\sbw\s+(\d+)\s*([KMG])bi?t?/){
	    $bw=$1.$2;
	}else{
	    die "Class $class without bandwidth at line $l\n";
	}
	if(/\suserbw\s+(\d+)\s*([KMG])bi?t?/){
	    $ubw=$1.$2;
	}
	if(/\sname\s+(\S+)/){
	    $name=$1;
	}
	$classes{$class}={id=>$class,bw=>$bw,ubw=>$ubw,name=>$name,l=>$l};
    }elsif(/^open\s+(\d+\.\d+\.\d+\.\d+)$/){
	$opened{$1}=1;
    }elsif(/^plan\s+(\d+)\D/){
	my $ubw=undef;
	my $pid=$1;
	if(/\suserbw\s+(\d+)\s*([KMG])bi?t?/){
	    $ubw=$1.$2;
	}
	$plans{$pid}={id=>$pid,ubw=>$ubw};
	
    }elsif(/^user\s+(\d+)\D/){
	my $uid=$1;
	if ($users{$uid}){
	    die "User $uid defined at line $l and ".$users{$uid}->{l}."\n";
	}
	my $bw=undef;
	my $class=undef;
	my $ip4=undef;
	my $plan=undef;
	if(/\sbw\s+(\d+)\s*([KMG])bi?t?/){
	    $bw=$1.$2;
	}
	if(/\sclass\s+(\d+)(\s|$)/){
	    $class=$1;
	    unless($classes{$class}){
		die "Noexistent class $class for user $uid at line $l\n";
	    }
	}else{
	    die "Classless user $uid at line $l\n";
	}
	if(/\ip4\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(\s|$)/){
	    $ip4=$1;
	}else{
	    die "User $uid without ip4 at line $l\n";
	}
	if(/\splan\s+(\S+)/){
	    $plan=$1;
	}
	$users{$uid}={id=>$uid,bw=>$bw,class=>$class,ip4=>$ip4,l=>$l,plan=>$plan};
#	print "User $1: ".$_."\n";
	
    }else{
	print "Error at line $l\n";
    }
}
my $planstart=200;

foreach(values %classes){
#    printf ("c %d %s %s\n",$_->{id},$_->{bw},$_->{ubw});
    printf ("# --- [ Class %d %s ] --- \n",$_->{id},$_->{name});
    printf ("ipfw pipe %d1 delete\n",($_->{id}+10)) if($allcfg);
    printf ("ipfw pipe %d2 delete\n",($_->{id}+10)) if($allcfg);
    printf ("ipfw queue %d1 delete\n",($_->{id}+10)) if($allcfg);
    printf ("ipfw queue %d2 delete\n",($_->{id}+10)) if($allcfg);

    printf ("ipfw pipe %d1 config bw %sbit/s noerror\n",($_->{id}+10),$_->{bw});
    printf ("ipfw pipe %d2 config bw %sbit/s noerror\n",($_->{id}+10),$_->{bw});
    printf ("ipfw queue %d1 config pipe %d1 weight 30 mask src-ip 0xffffffff noerror\n",($_->{id}+10),($_->{id}+10));
    printf ("ipfw queue %d2 config pipe %d2 weight 30 mask dst-ip 0xffffffff noerror\n",($_->{id}+10),($_->{id}+10));
    if($_->{ubw}){
	printf ("ipfw pipe %d3 delete\n",($_->{id}+10)) if($allcfg);
        printf ("ipfw pipe %d4 delete\n",($_->{id}+10)) if($allcfg);
        printf ("ipfw pipe %d3 config bw %sbit/s mask src-ip 0xffffffff noerror\n",($_->{id}+10),$_->{ubw});
	printf ("ipfw pipe %d4 config bw %sbit/s mask dst-ip 0xffffffff noerror\n",($_->{id}+10),$_->{ubw});
    }
    printf ("\n");
}
my $coeff=1;

foreach(values %plans){
#    printf ("c %d %s %s\n",$_->{id},$_->{bw},$_->{ubw});
    printf ("# --- [ plan %d ] --- \n",$_->{id});
    printf ("ipfw pipe %d delete\n",(($_->{id}*2)+$planstart)) if($allcfg);
    printf ("ipfw pipe %d delete\n",(($_->{id}*2)+$planstart+1)) if($allcfg);
    my $cbw=$_->{ubw};
    if($cbw=~/^(\d+)(\w?)$/){
	$cbw=($1*$coeff).$2;
    }
    printf ("ipfw pipe %d config bw %sbit/s mask src-ip 0xffffffff noerror\n",(($_->{id}*2)+$planstart),$cbw);
    printf ("ipfw pipe %d config bw %sbit/s mask dst-ip 0xffffffff noerror\n",(($_->{id}*2)+$planstart+1),$cbw);
    printf ("\n");
}

printf ("# --- [ Users ] --- \n");
my %o15;
open(FH,'ipfw table 15 list|');
while(<FH>){
    chomp;
    if(/(\d+\.\d+\.\d+\.\d+)\/\d+\s(\d+)/){
	$o15{$1}=$2;
    }else{
	die($_);
    }
}
close(FH);
foreach(keys %o15){
    unless(defined($opened{$_})){
        printf ("ipfw table 15 delete %s/32\n",$_);
    }
}
foreach(keys %opened){
    unless(defined($o15{$_})){
        printf ("ipfw table 15 add %s/32\n",$_);
    }
}

my %n10;
my %n11;
my %n12;
my %n13;

foreach(values %users){
#    printf ("u %d c %s %s %s\n",$_->{id},$_->{class},$_->{ip4},$_->{bw});
    if($_->{bw}){
        printf ("ipfw pipe %d config bw %sbit/s noerror #user %d personal\n",$maxu+($_->{id}*2),$_->{bw},$_->{id});
        printf ("ipfw pipe %d config bw %sbit/s noerror #user %d personal\n",$maxu+1+($_->{id}*2),$_->{bw},$_->{id});
	$n12{$_->{ip4}}=$maxu+($_->{id}*2);
	$n13{$_->{ip4}}=$maxu+($_->{id}*2)+1;
    }elsif($_->{plan}){
	$n12{$_->{ip4}}=(($_->{plan}*2)+$planstart);
	$n13{$_->{ip4}}=(($_->{plan}*2)+$planstart)+1;
    }elsif($classes{$_->{class}}->{ubw}){
	$n12{$_->{ip4}}=(($_->{class}+10)*10)+3;
	$n13{$_->{ip4}}=(($_->{class}+10)*10)+4;
    }
    $n10{$_->{ip4}}=(($_->{class}+10)*10)+1;
    $n11{$_->{ip4}}=(($_->{class}+10)*10)+2;
}
my %o10;
open(FH,'ipfw table 10 list|');
while(<FH>){
    chomp;
    if(/(\d+\.\d+\.\d+\.\d+)\/\d+\s(\d+)/){
	$o10{$1}=$2;
    }else{
	die($_);
    }
}
close(FH);
my %o11;
open(FH,'ipfw table 11 list|');
while(<FH>){
    chomp;
    if(/(\d+\.\d+\.\d+\.\d+)\/\d+\s(\d+)/){
	$o11{$1}=$2;
    }else{
	die($_);
    }
}
close(FH);
my %o12;
open(FH,'ipfw table 12 list|');
while(<FH>){
    chomp;
    if(/(\d+\.\d+\.\d+\.\d+)\/\d+\s(\d+)/){
	$o12{$1}=$2;
    }else{
	die($_);
    }
}
close(FH);
my %o13;
open(FH,'ipfw table 13 list|');
while(<FH>){
    chomp;
    if(/(\d+\.\d+\.\d+\.\d+)\/\d+\s(\d+)/){
	$o13{$1}=$2;
    }else{
	die($_);
    }
}
close(FH);


foreach(keys %o10){
    if(!defined($n10{$_}) || $n10{$_}!=$o10{$_}){
        printf ("ipfw table 10 delete %s/32 #%d\n",$_,$o10{$_});
    }
}
foreach(keys %n10){
    if(!defined($o10{$_}) || $n10{$_}!=$o10{$_}){
        printf ("ipfw table 10 add %s/32 %d\n",$_,$n10{$_});
    }
}

foreach(keys %o11){
    if(!defined($n11{$_}) || $n11{$_}!=$o11{$_}){
        printf ("ipfw table 11 delete %s/32 #%d\n",$_,$o11{$_});
    }
}
foreach(keys %n11){
    if(!defined($o11{$_}) || $n11{$_}!=$o11{$_}){
        printf ("ipfw table 11 add %s/32 %d\n",$_,$n11{$_});
    }
}


foreach(keys %o12){
    if(!defined($n12{$_}) || $n12{$_}!=$o12{$_}){
        printf ("ipfw table 12 delete %s/32 #%d\n",$_,$o12{$_});
    }
}
foreach(keys %n12){
    if(!defined($o12{$_}) || $n12{$_}!=$o12{$_}){
        printf ("ipfw table 12 add %s/32 %d\n",$_,$n12{$_});
    }
}


foreach(keys %o13){
    if(!defined($n13{$_}) || $n13{$_}!=$o13{$_}){
        printf ("ipfw table 13 delete %s/32 #%d\n",$_,$o13{$_});
    }
}
foreach(keys %n13){
    if(!defined($o13{$_}) || $n13{$_}!=$o13{$_}){
        printf ("ipfw table 13 add %s/32 %d\n",$_,$n13{$_});
    }
}


#printf '#em0 - external, em1 - internal'."\n";
#printf "ipfw add 501  deny ip from not table\\(%d\\) to any out recv em1 xmit em0\n",15;
#printf "ipfw add 1001 pipe tablearg ip from table\\(%d\\) to any  out recv em1 xmit em0\n",12;
#printf "ipfw add 1002 pipe tablearg ip from any to table\\(%d\\)  out recv em0 xmit em1\n",13;
#printf "ipfw add 1010 queue tablearg ip from table\\(%d\\) to any out recv em1 xmit em0\n",10;
#printf "ipfw add 1011 queue tablearg ip from any to table\\(%d\\) out recv em0 xmit em1\n",11;
if($allcfg){
foreach(values %classes){
    printf "ipfw delete 201%d\n",$_->{id};
    printf "ipfw delete 202%d\n",$_->{id};
    printf "ipfw add 201%d allow ip from table\\(%d,%d1\\) to any out recv em1 xmit em0\n",$_->{id},10,$_->{id}+10;
    printf "ipfw add 202%d allow ip from any to table\\(%d,%d2\\) out recv em0 xmit em1\n",$_->{id},11,$_->{id}+10;
}
}
#printf "ipfw add 2019 allow ip from any to any out recv em1 xmit em0\n";
#printf "ipfw add 2029 allow ip from any to any out recv em0 xmit em1\n";

