At this point, the route-based QoS configuration is complete. However, everyone likes to see colorful graphs of what the servers are doing. Thus, here's how to use an MRTG or RRD tool to graph the outbound traffic flows. The script below gathers queue counter data, converts it into bit counts, and makes it available to snmpd.
Example 5. trafgraf.pl
#!/usr/bin/perl -w
# trafgraf.pl
# a bridge between the iproute2 tool tc and snmp
# base source: http://www.docum.org/stef.coene/qos/gui/
# but *heavily* modified
use strict;
sub nics ();
sub disp ($$);
#------------------------------------------------------------#
my $base = "/opt/trafgraf";
my $tccmd = "/sbin/tc";
my $ipcmd = "/sbin/ip";
my $debug = 0;
my ($key,%bytes,%tokens,$number);
my %parent;
my %nics = nics() ;
my $reqtype = "";
my $reqif = "";
my $reqqdisc = "";
#------------------------------------------------------------#
if ($#ARGV != 1) {
# not called from snmp
} else {
# set reqtype 'walk' or 'get'
if ($ARGV[0] eq '-g') {
$reqtype = "get";
} elsif ($ARGV[0] eq '-n') {
$reqtype = "walk";
} else {
$reqtype = "";
}
($reqif,$reqqdisc) = $ARGV[1] =~ /^\.1\.3\.6\.1\.4\.1\.2021\.255\.(\d+)\.*(\d*)/;
}
#------------------------------------------------------------#
# get all the info from all interfaces (qdisc + class)
my (@qdisc,@class,$line,$interface);
my ($byts,$pkts,$drops,$olimits);
foreach $interface (keys(%nics)) {
$interface = $nics{$interface};
@qdisc = `$tccmd -s -d qdisc show dev $interface`;
@class = `$tccmd -s -d class show dev $interface`;
$number = "";
if ($debug) {
print "start interface: $interface\n";
}
foreach $line (@qdisc) {
chomp($line);
if ($line =~ /^\s*$/) { next; }
if ($debug) {
print "qdisc line: $line\n";
}
# get qdisc id
# qdisc sfq 8045: quantum 1514b limit 128p flows 128/1024
if ($line =~ /^\s*qdisc/) {
($number) = $line =~ /\s+([A-Za-z0-9]+:)\s+/o;
}
# Sent 0 bytes 0 pkts (dropped 0, overlimits 0)
if (($line =~ /^\s*Sent/) && $number) {
($byts,$pkts,$drops,$olimits) = $line =~ /Sent\s+(\d+)\s+bytes\s+(\d+)\s+pkts\s+\(dropped\s+(\d+),\s+overlimits\s+(\d+)\)/;
$bytes{"$interface $number"} = "$byts $pkts $drops $olimits";
if ($debug) {
print "$interface $number " . $bytes{"$interface $number"} . "\n";
}
$number = "";
}
}
# let's skip the class info
next;
foreach $line (@class) {
chomp ($line);
if ($line =~ /^\s*$/) { next; }
if ($debug) {
print "class line: $line\n";
}
$line =~ /\s+([A-Za-z0-9]+:[A-Za-z0-9]+)\s+root/o
and $number = $1
and $parent{"$interface $number"} = (split(":",$number))[0] . ":" ;
$line =~ /\s+([A-Za-z0-9]+:[A-Za-z0-9]+)\s+parent\s+([A-Za-z0-9]+:[A-Za-z0-9]+)\s+leaf\s+([A-Za-z0-9]+:)\s+/o
and $number = $1
and $parent{"$interface $number"} = $2
and $parent{"$interface $3"} = $number
and next ;
$line =~ /\s+([A-Za-z0-9]+:[A-Za-z0-9]+)\s+parent\s+([A-Za-z0-9]+:[A-Za-z0-9]+)\s+/o
and $number = $1
and $parent{"$interface $number"} = $2
and next ;
$line =~ /Sent\s+(\d+)\s+bytes\s+(\d+)\s+pkts\s+\(dropped\s+(\d+),\s+overlimits\s+(\d+)\)/
and $bytes{"$interface $number"} = "$1 $2 $3 $4"
and next ;
$line =~ /lended:\s+(\d+)\s+borrowed:\s+(\d+)\s+giants:\s+(\d+)/
and $lended{"$interface $number"} = "$1 $2 $3"
and next ;
$line =~ /tokens:\s+(\d+)\s+ctokens:\s+(\d+)/
and $tokens{"$interface $number"} = "$1 $2"
and next ;
}
}
#------------------------------------------------------------#
# figure out what to send back
# probably run manually, don't try and format results
if ($#ARGV < 1) {
if ($debug) {
print "Exiting due to manual run\n";
}
exit;
}
if (($reqtype eq "get") && ($ARGV[1] eq ".1.3.6.1.4.1.2021.255")) {
if ($debug) {
print "Exiting due reqtype == get\n";
}
exit;
}
# .1.3.6.1.4.1.2021.255.<interface>.<qdisc>
if ($debug) {
print "reqif: $reqif\n";
print "reqqdisc: $reqqdisc\n";
}
if (!defined($reqif)) {
if ($debug) {
print "Oops, no request interface\n";
}
exit;
}
if ($reqqdisc) {
disp($reqif,$reqqdisc);
} else {
disp($reqif,0);
}
exit;
#------------------------------------------------------------#
# print output
sub disp($$) {
my $if = shift();
my $id = shift();
my $key = "eth" . $if . " $id:";
my ($val,@keys,@tokens,$count);
# if the id is 0, check for a 0: class
if (($id == 0) && (!exists($bytes{$key}))) {
@keys = sort(keys(%bytes));
$key = $keys[0];
($id) = $key =~ /^eth\d+ (\d+):$/;
}
$val = $bytes{$key};
if ($debug) {
print "bytes{$key} => $bytes{$key}\n";
}
if (defined($val)) {
@tokens = split(/\s+/,$val);
$count = $tokens[0];
} else {
$count = 0;
}
# adjust for 32 bit counter
if ($count > 4294967295) {
$count = $count % 4294967295;
}
print ".1.3.6.1.4.1.2021.255.$if.$id\n";
print "counter\n";
print "$count\n";
}
#------------------------------------------------------------#
# Find all configured nics
sub nics() {
my @niclist = `$ipcmd link show`;
my (%nic,$line);
foreach $line (@niclist) {
$line =~ /^([0-9]+):[ ]+([a-z0-9]+)/ or next;
$nic{$1} = $2;
}
return(%nic);
}
|
The following lines should be appended to snmpd.conf
# zebra & bgpd smux configuration
smuxpeer 1.3.6.1.4.1.3317.1.2.1 zebra
smuxpeer 1.3.6.1.4.1.3317.1.2.2 zebra_bgpd
# qos traffic stats
pass .1.3.6.1.4.1.2021.255 /opt/trafgraf/bin/snmp.pl
|

Sample graph of traffic flows generated using Cricket
The graph shows three categories: green, purple, and dark blue. From the example scripts, the green line is local network traffic, the purple is Internet2, and the blue is any other destination (regular commodity Internet traffic). The orange line represents combined traffic from the three categories. The green and purple categories are allocated higher levels of bandwidth than the blue flow.