Simple Quality of Service (QoS) on a DSL link
using GNU/Linux Debian 3.0


ADSL is a paradize for QoS: the only queue that you control (your queue going out to your ISP) is the number one bottleneck, as soon as you start to be more than just a client.
We will see here how to prioritize outgoing traffic on a DSL link, with one of the simplest Linux qdisc (queueing discipline): PRIO.
From tc's man page:

 The  PRIO qdisc is a simple classful queueing discipline that contains
 an arbitrary number of classes of differing priority. The classes  are
 dequeued  in  numerical descending order of priority. PRIO is a sched-
 uler and never delays packets - it is a work-conserving qdisc,  though
 the qdiscs contained in the classes may not be.

 Very  useful  for  lowering  latency when there is no need for slowing
 down traffic.

Software Requirements

Y ou will need the Debian iproute package, and a kernel compiled with the QoS modules.
My approach has been to stick with a 2.4.25 (Ocin, stop laughing at my issues with 2.6.x!)
and put everything I could about scheduling:

#
# QoS and/or fair queueing
#
CONFIG_NET_SCHED=y
CONFIG_NET_SCH_CBQ=m
CONFIG_NET_SCH_HTB=m
CONFIG_NET_SCH_CSZ=m
CONFIG_NET_SCH_HFSC=m
CONFIG_NET_SCH_PRIO=m
CONFIG_NET_SCH_RED=m
CONFIG_NET_SCH_SFQ=m
CONFIG_NET_SCH_TEQL=m
CONFIG_NET_SCH_TBF=m
CONFIG_NET_SCH_GRED=m
CONFIG_NET_SCH_DSMARK=m
CONFIG_NET_SCH_INGRESS=m
CONFIG_NET_QOS=y
CONFIG_NET_ESTIMATOR=y
CONFIG_NET_CLS=y
CONFIG_NET_CLS_TCINDEX=m
CONFIG_NET_CLS_ROUTE4=m
CONFIG_NET_CLS_ROUTE=y
CONFIG_NET_CLS_FW=m
CONFIG_NET_CLS_U32=m
CONFIG_NET_CLS_RSVP=m
CONFIG_NET_CLS_RSVP6=m
CONFIG_NET_CLS_POLICE=y

Prio QDisc

S hameless as I am, I just took the very good script by Nicolas Dade and modified it to my needs.
Basically, I did not need the stop/start feature and the arp spoofing, because my Debian box is my router.
And my wife complains if her http traffic is not served with due diligence.
But... but... What do I bring on the table?
Well, it is cute to deploy QoS, but if you do not see a measurable effect on the packets themselves you would feel frustrated...
So, here is a very stupid-and-sub-optimal QoS monitoring script that will do the trick:

#!/usr/bin/perl -w
use strict;
my (%sent, %oldsent, %comp, %drop, %olddrop);
while (1) {
 open FILE, "/sbin/tc -s qdisc show dev ppp0 |";
 my $queue;
 while () {
  $queue = $1 if ($_ =~ /qdisc ([^:]+):/);
  $sent{$queue} = $1 if ($_ =~ /Sent (\d+) bytes/);
  $comp{$queue} = $1 if ($_ =~ /backlog (\d+)p/);
  $drop{$queue} = $1 if ($_ =~ /dropped (\d+),/);
 }
 close FILE;
 foreach my $q (keys %sent) {
  print "  $q",
        "\t",
        scalar ($sent{$q} -
                ((defined $oldsent{$q}) ? $oldsent{$q} : 0)),
        "\t\t",
        "Backlog: ",
        (defined $comp{$q}) ? $comp{$q} : 0,
        "p\t\tDropped: ",
        scalar ($drop{$q} -
                ((defined $olddrop{$q}) ? $olddrop{$q} : 0)),
        "\n";
 }
 %oldsent=%sent;
 %olddrop=%drop;
 sleep(1);
 print "\n";
}

I know that GNU/Awk would be faster / more adapted and I know that I could put that in nice ucd-snmp oids to get graphs with MRTG or rrdtool.
I just want a quick and dirty one, to see my packets being queued and everything.
So: Run your QoS configuration script (here is my adaptation of Nicolas Dade's script, I threw it under /etc/ppp/ip-up.d), and then run the QoS monitoring script.

The kind of output you need to expect:

  prio 1        11055           Backlog: 9p             Dropped: 0
  pfifo 11      74              Backlog: 0p             Dropped: 0
  pfifo 12      190             Backlog: 0p             Dropped: 0
  tbf 13        10791           Backlog: 9p             Dropped: 0

  prio 1        7813            Backlog: 9p             Dropped: 0
  pfifo 11      0               Backlog: 0p             Dropped: 0
  pfifo 12      366             Backlog: 0p             Dropped: 0
  tbf 13        7447            Backlog: 9p             Dropped: 0




   prio 1 is your main qdisc. It provides the aggregated statistics.
You see, per one-second interval,



   the total traffic in bytes for your ppp0 interface,

   the total backlog (packets queued for later delivery) and

   the total drops


   pfifo 11 is a qdisc just to provide statistics on band #1.
That's where the highest priority traffic goes, typically ssh.

   pfifo 12 is for "normal" traffic, band #2. I use it for http and RealPlayer (it needs to be configured for that).

   tbf 13 is not called pfifo 13 because the script attaches a Token Bucket Filter to that band, to tune its behaviour.

Where is the fun?

Y ou can start playing with the parameters, now.




   In particular, make sure your ssh traffic goes in the right band. Launch a ssh connection, and look at what traffic increases.

   You have some background traffic, like GNUtella? It should be in band 3.

   Start using your ssh, and look at the backlog increase for your GNUtella, not for your band #1.

   Reduce the "latency 500ms" in the tbf qdisc, and you will see that you start to drop earlier. Increase, and you will see the backlog increase but the drops decrease.

What's next?

P rio is pretty basic, but the Linux kernel can implement QoS mechanisms as complicated as you want.
Hint: this Hierarchy Token Bucket looks quite fun, also!
As for using rrdtool, well, I have started with this scripts to collect the measurements, this one to draw the graphs on a different project and in French.