#!/usr/bin/perl -w

use strict;

my $OUTFILE='rc.qos';
my $RANGESTART='192.168.1';
my $RANGEEND='192.168.254';
my @EXCLUDE;
#my @EXCLUDE=('192.168.10.0-192.168.10.255','192.168.15.192-192.168.15.255',
#	     '192.168.21.25');
my $IPSTART='1';
my $IPEND='254';
my $IF='eth0';
my $TC='/root/bin/tc';
my $RATE='30kbps';

sub init();
sub exclude($);

my ($oct12,$oct3start,$oct3end,$oct4start,$oct4end);

init();

open(FH,">$OUTFILE") or die("cannot create $OUTFILE: $!");

print FH <<EOF;
#!/bin/bash

#--- Cleaning ---
$TC filter del dev $IF protocol ip parent 13:0 prio 5 2>/dev/null
$TC filter del dev $IF protocol ip parent 1:0 prio 4 2>/dev/null
$TC qdisc del dev $IF root 2>/dev/null
#--- End cleaning ---

#root qdisc
$TC qdisc add dev $IF root handle 1: prio bands 3

$TC qdisc add dev $IF parent 1:1 handle 11: pfifo
$TC qdisc add dev $IF parent 1:2 handle 12: pfifo
$TC qdisc add dev $IF parent 1:3 handle 13: htb

EOF

print FH "#--- classes for user ips ---\n";
for(my $i=$oct3start;$i<=$oct3end;$i++) {
 for(my $j=$oct4start;$j<=$oct4end;$j++) {
   next if (exclude("$oct12.$i.$j"));
   printf FH "$TC class add dev $IF parent 13: classid 13:%02x%02x htb rate $RATE\n",
     $i,$j;
   printf FH "$TC qdisc add dev $IF parent 13:%02x%02x handle %02x%02x pfifo\n",
     $i,$j,$i,$j;
 }
}
print FH "#--- end of classes for user ips ---\n";

print FH <<EOF;
#--- filters ---
$TC filter add dev $IF parent 1:0 protocol ip prio 4 u32 \\
    match ip sport 119 0xffff flowid 1:3

$TC filter add dev $IF parent 13:0 prio 5 protocol ip u32
$TC filter add dev $IF parent 13:0 prio 5 handle 2: protocol ip u32 divisor 256
$TC filter add dev $IF parent 13:0 prio 5 handle 3: protocol ip u32 divisor 256

EOF

for(my $i=$oct3start;$i<=$oct3end;$i++) {
 for(my $j=$oct4start;$j<=$oct4end;$j++) {
   next if (exclude("$oct12.$i.$j"));
   printf FH "$TC filter add dev $IF parent 13:0 prio 5 protocol ip u32 ht 2:%02x: ht 3:%02x: match ip dst $oct12.$i.$j/32 flowid 13:%02x%02x\n",
      $i,$j,$i,$j;
 }
}

print FH <<EOF;

$TC filter add dev $IF protocol ip parent 13:0 prio 5 u32 ht 800:: \\
    match ip dst 66.114.0.0/16 hashkey mask 0x0000ff00 at 16 link 2:
$TC filter add dev $IF protocol ip parent 13:0 prio 5 u32 ht 800:: \\
    match ip dst 66.114.0.0/16 hashkey mask 0x000000ff at 16 link 3:
#--- end of filters ---
EOF

close(FH);


#--- Subroutines ---

sub init() {
  ($oct12,$oct3start)=($1,$2) if ($RANGESTART=~/^(\d+\.\d+)\.(\d+)/);
  $oct3end=$1 if ($RANGEEND=~/^\d+\.\d+\.(\d+)/);
  $oct4start=$IPSTART;
  $oct4end=$IPEND;
  if (!$oct12 or !$oct3start or !$oct3end) {
     die("wrong configuration values");
  }
}

sub exclude($) {
  my $checkip=shift;
  my ($c1,$c2,$c3,$c4);

  if ($checkip=~/^(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
      ($c1,$c2,$c3,$c4)=($1,$2,$3,$4) 
  } else {
      die("$checkip is not an ip address");
  }

  foreach my $exclude (@EXCLUDE) {
    my ($rangestart,$rangeend);
    if ($exclude=~/^\s*(\d+\.\d+.\d+\.\d+)\s*-\s*(\d+\.\d+\.\d+\.\d+)\s*$/) 
    {
       ($rangestart,$rangeend)=($1,$2);
    }
    elsif ($exclude=~/^\s*(\d+\.\d+.\d+\.\d+)\s*$/) {
       ($rangestart,$rangeend)=($1,$1);
    } else {
       next;
    }

    my ($s1,$s2,$s3,$s4)=($1,$2,$3,$4) 
 	if ($rangestart=~/^(\d+)\.(\d+)\.(\d+)\.(\d+)/);
    my ($e1,$e2,$e3,$e4)=($1,$2,$3,$4) 
 	if ($rangeend=~/^(\d+)\.(\d+)\.(\d+)\.(\d+)/);
    if ( $s1!=$e1 or $s2!=$e2) { die("wrong exclude values"); }
    if ( $s1!=$c1 or $s2!=$c2) { return 0; }
    if ( $c3>=$s3 and $c3<=$e3) {
       if ( $c4>=$s4 and $c4 <=$e4 ) {
	  return 1;
       }
    }
  }
  return 0;
}