RE: TC Hashing Filters

Linux Advanced Routing and Traffic Control

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



gypsy wrote:
>Try setting classid to 0xffff and decrement rather than increment it.
>
>Pepper the script with 
>debug (print or echo) lines
>error traps that exit the script on error.
>
>Is what you posted entire so that if I extract it from the message then
>it should run?

	It's really tough for me to convey what I've done so far mainly
because when I get in "Troubleshoot Mode" I really tend to forget what
I've tried and the results of those efforts after I've done them. This
is because if it doesn't work, I rule it out and go on to the next thing
until I find out what the problem is. The reason I say this is because I
didn't really tell you specifically what I've done to troubleshoot. Only
that I found out where the problem was. For this, I apologize. So let me
try to be as specific as possible.
This is what the script does step by step:
1. Connect to the provisioning database (MySQL)
2. Define subroutines
	a. SelectSQL - Subroutine for placing all information from a sql
query into a variable.
	b. SelectSingleSQL - Subroutine for placing one piece of
information from a sql query into a variable.
	c. SimpleSQL - Subroutine for making a sql query
	d. Action - Subroutine for performing a system action and
outputting any errors to an array for later use.
3. Remove existing root qdisc and add a new one (clears all information
currently stored).
4. Create transit class and hash table/filter.
5. For creating the individual classes for each rate, we have it connect
to our database and add a class for each rate located in that database.
This is so it can be dynamic in case we need to add new classes down the
road.
6. This is where the script grabs all the accounts from our database by
modem. The modem table holds the rate for each customer. Then the script
compares those modems to the public IPs assigned to that customer and
adds the tc command to limit that IP based on the modem rate.
7. Take all the errors from any "Action" and output them. (This emails
to me directly when there's a problem).
8. There's a bunch of stuff here for promotional rates we're running
that is unimportant to the current problem I'm having.

	The error appears at step 6. For each modem in our database, it
checks the IPs assigned to it. For each of those, it runs: tc filter add
dev $dev protocol ip parent 1: u32 ht 2:$table: match ip dst $ip flowid
1:$classid
I added a counter in there and an exit command in the "Action"
subroutine so that when the script errors, it exits and shows me how
many IPs tc has added before it produced an error. The number was 2045
and the error was RTNETLINK answers: File exists.

	As for your suggestion about the classid, I'm a bit confused as
to what you mean about decrementing it. Could you be a little more
specific on where this is in the script?

	I have attached the script in its entirety so you can see it and
maybe figure out what is wrong. The only problem with running it would
be connecting to the database. If you want, I can put up a mock database
and you can connect to that for testing purposes. The problem with this
is that I'd have to populate it with about 3000 entries for you to see
the error I'm seeing. I have removed my traps and counters so you can
see what the script was originally. I'll comment where the error occurs.
Thank you all once again for your help and time. It is very much
appreciated.

#!/usr/bin/perl
# 
# TC Helper Script: Written by Mike Davis & Adam Towarnyckyj
#
#   Synchronizes data rates with MySQL server and applies hourly.
#

### Configuration Section ###


$dev = "eth1";
$tc = "/sbin/tc";
$mysql_host = "sql database";
$mysql_db = "databse";
$mysql_user = "user";
$mysql_pass = "password";


### END Configuration Section ###

use POSIX qw(strftime);

# Database connect and define subroutines

use DBI;

$dsn = "DBI:mysql:database=$mysql_db;hostname=$mysql_host";
$dbh = DBI->connect($dsn, $mysql_user, $mysql_pass) || die "Can't
connect to database: " . DBI->errstr;

# Subroutine for placing all information from a sql query into a
variable.
sub SelectSQL {
  my($sql) = @_;
  my @MATCHES, $hash;
  $sth = $dbh->prepare("$sql");
  $sth->execute();
  while ($hash = $sth->fetchrow_hashref) {
        push @MATCHES, $hash;
  }
  return @MATCHES;
}

sub SelectSingleSQL {
  my($sql) = @_;
  my($gotit, $return, $hash);
  $sth = $dbh->prepare("$sql");
  $sth->execute();
  while ($hash = $sth->fetchrow_array) {
        unless ($gotit) {
          $return = $hash;
          $gotit++;
        } else { warn "got multiple SQL returns when exepecting only
one"; }
  }
  return $return;
}

sub SimpleSQL {
  my($sql) = $_[0];
  my $rows_affected;
  $rows_affected = $dbh->do($sql);
  return $rows_affected;
}

sub Action {
  my($action) = @_;
#  print"Performing: $action\n";
  $warn=`$action 2>&1`;
  if ($warn) {
    chomp($warn);
    $prepare = "ERROR: $warn. Command was: $action";
#    print"WHOOPS: $warn\n";
    push @WARNING, $prepare;
  }
}

### Ok, now we start having fun.  Let's rebuild the tc tree.

# Remove existing tree and add the root.

Action("$tc qdisc del dev $dev root");
Action("$tc qdisc add dev $dev root handle 1:0 cbq bandwidth 200mbit
avpkt 1000");

# Create 'transit class', tc hash tables, and hash filter

Action("$tc class add dev $dev parent 1: classid 1:2 cbq bandwidth
200Mbit rate 200MBit allot 1514 weight 2Mbit prio 8 maxburst 20 avpkt
1000");
Action("$tc filter add dev $dev parent 1: handle 2: protocol ip u32
divisor 256");
Action("$tc filter add dev $dev protocol ip parent 1: u32 match ip dst
0.0.0.0/0 hashkey mask 0x000000ff at 16 link 2:");

# Create classes for rate groups

@RATES = SelectSQL("SELECT dsrate FROM dsrate");
my $classid = 3;
foreach $dsrate (@RATES) {
	$ds = $$dsrate{dsrate};
	if ($ds == "0" || $ds == "1") {
		next;
	}
	Action("$tc class add dev eth1 parent 1: classid 1:$classid cbq
bandwidth 200Mbit rate $$dsrate{dsrate}Kbit allot 1514 prio 5 maxburst
20 avpkt 1000 bounded");
	$rates{$ds} = $classid;
	$classid++;
}

# Get our list of accounts

@MODEMS = SelectSQL("SELECT mid, dsrate FROM modems");

# Figure out account IPs and put 'em in!
foreach $modem (@MODEMS) {
	if ($$modem{dsrate} == "0" || $$modem{dsrate} == "1") {
		next;
	}
	my @COMPUTERS = SelectSQL("SELECT ipid FROM computers WHERE
mid='$$modem{mid}'");
	foreach $computer (@COMPUTERS) {
		my $ip = SelectSingleSQL("SELECT ipaddr FROM ips WHERE
ipid='$$computer{ipid}'");
		@octets = split(/\./,$ip);
		$table = $octets[3];
		$table = sprintf("%X", $table);
		$classid = $rates{$$modem{dsrate}};
		Action("$tc filter add dev $dev protocol ip parent 1:
u32 ht 2:$table: match ip dst $ip flowid 1:$classid");  ### Here is
where it errors after 2045 entries ###
	}
}

if (@WARNING) {
  print"WARNING: TCHELPER produced errors!  See below:\n @WARNING\n";
}

#
# Cool, everyone is now limited.  Let's do some up-keep on the promo
rates.
#

# First we check accounts with a promo rate and no promo code, and fill
it in.

@PROMORATES = SelectSQL("SELECT dsrate,drpromo FROM dsrate WHERE drpromo
!='0'");

$month=strftime "%m", localtime;
$day=strftime "%d", localtime;
$year=strftime "%Y", localtime;
$today="$year" . "$month" . "$day";

foreach $rate (@PROMORATES) {
        ($exprate,$expdays) = split("-",$$rate{drpromo});
        $expdate = $day + $expdays;
        $expmonth = $month;
        $expyear = $year;
        while ($expdate > "30") {
                $expmonth++;
                $expdate = $expdate - 30;
        }
        while ($expmonth > "12") {
                $expyear++;
                $expmonth = $expmonth - 12;
        }
        $absexpdate = "$expyear" . "$expmonth" . "$expdate";
        @UNMARKED = SelectSQL("SELECT mid FROM modems WHERE
dsrate='$$rate{dsrate}' AND promocode=''");
        foreach $mark (@UNMARKED) {
                $query = SimpleSQL("UPDATE modems SET
promocode='$exprate-$absexpdate' WHERE mid='$$mark{mid}'");
        }
}

# Now we check for expired promo codes and reset their rate.

@PROMOACCTS = SelectSQL("SELECT mid,promocode FROM modems WHERE
promocode != ''");

foreach $acct (@PROMOACCTS) {
        ($exprate,$expdate) = split("-",$$acct{promocode});
        if ($expdate <= $today) {
                $query = SimpleSQL("UPDATE modems SET dsrate='$exprate',
promocode='' WHERE mid='$$acct{mid}'");
        }
}

# Exit Nice and clean.

$dbh->disconnect;
exit(0);

_______________________________________________
LARTC mailing list / LARTC@xxxxxxxxxxxxxxx
http://mailman.ds9a.nl/mailman/listinfo/lartc HOWTO: http://lartc.org/

[Index of Archives]     [LARTC Home Page]     [Netfilter]     [Netfilter Development]     [Network Development]     [Bugtraq]     [GCC Help]     [Yosemite News]     [Linux Kernel]     [Fedora Users]
  Powered by Linux