Mathematics, philosophy, programming, in-line skating and everything in between. More about me…

I write about

English | Czech
Choose your language. I write in English, but I translate most of my articles to Czech as well. Zvolte si jazyk. Píšu anglicky, ale většinu svých článků překládám i do češtiny.

Visualizing Linux Traffic Control Setup

In the past six months I had to deal with Linux traffic control (TC) a lot. I was literally blown away by the advanced features of the system. Of course, the versatility comes for the usual price: complexity. I definitely do not regret the time spent on understanding the principles of classes, qdiscs, filters, major and minor numbers, etc. However, I think I have found a way to work with them more easily.

When you want to see your TC configuration in full, you normally have to list qdiscs, classes, and filters separately and mentally piece the outputs together. For example:

$ tc qdisc show dev eth0
qdisc htb 1: root r2q 10 default 10 direct_packets_stat 0
qdisc sfq 10: parent 1:10 limit 127p quantum 1514b perturb 10sec
qdisc sfq 11: parent 1:11 limit 127p quantum 1514b perturb 10sec
qdisc sfq 19: parent 1:19 limit 127p quantum 1514b perturb 10sec
qdisc sfq 31: parent 1:31 limit 127p quantum 1514b perturb 10sec
$ tc class show dev eth0
class htb 1:11 parent 1:1 leaf 11: prio 0 rate 256000bit ceil 256000bit burst 15Kb cburst 1599b
class htb 1:10 parent 1:1 leaf 10: prio 0 rate 128000bit ceil 128000bit burst 15Kb cburst 1599b
class htb 1:1 root rate 10000Kbit ceil 10000Kbit burst 15Kb cburst 1600b
class htb 1:31 parent 1:1 leaf 31: prio 0 rate 128000bit ceil 128000bit burst 15Kb cburst 1599b
class htb 1:19 parent 1:1 leaf 19: prio 0 rate 512000bit ceil 512000bit burst 15Kb cburst 1599b
$ tc filter show dev eth0
filter parent 1: protocol ip pref 1 fw
filter parent 1: protocol ip pref 1 fw handle 0x1 classid 1:11
filter parent 1: protocol ip pref 1 fw handle 0x9 classid 1:19
filter parent 1: protocol ip pref 1 fw handle 0x15 classid 1:31

Since there is a certain hierarchy in TC configurations, I soon found myself drawing TC setups in trees. The idea to replace paper and pencil with Graphviz came not long after. Writing a simple script that parses the output of tc and converts it into a graph turned out to be surprisingly easy. Behold the glory of 181 lines of Python that beareth the name tcviz.

Note: Actually, I found a PERL script somewhere on the net that was doing precisely the same. However, the output was dead ugly for my eye. Tweaking the script was out of question since the PERL source looked even uglier. Hence, I took owls to Athens and wrote my own piece of software. It was fun.

The above configuration can be magically translated by tcviz into this picture:

tcviz-generated graph example

Qdiscs and classes are displayed as boxes and ellipses, respectively. Filters are represented by arrows that point from the parent to the target of the filter.

The script only generates commands for dot (a part of Graphviz). To actually generate the graph, the output of tcviz has to be passed to dot. For instance:

$ ./tcviz.py eth0 | dot -Tpng > tc.png

To display the graph without saving it, one could use display from the ImageMagick package:

$ ./tcviz.py eth0 | dot -Tpng | display

tcviz is still in early stage of development. I have only tested it with tc versions iproute2-ss060323 and iproute2-ss071016. It’s possible that the format of tc’s output is different in other versions and tcviz won’t understand it. Bug reports are welcome. I even have a vague development plan, so there might be some new features in the future (not just bug-fix releases).

Feel free to give tcviz a try. I’ll be happy to hear your opinions and suggestions.

Project site

P.S.: The best resource on TC out there is Linux Advanced Routing & Traffic Control.

April 5, MMIX — Linux, Programming, Projects and Python. 18 comments.

18 comments Add your own…

(avatar) Dave May 22, MMIX
Hi,

 When I try you program it gives me an error at:
[root@BuffyTheVampireSlayer tcviz-1.0]# python tcviz.py eth0 | dot -Tpng  tc.png
-bash: dot: command not found
Traceback (most recent call last):
 File "tcviz.py", line 12, in ?
   from Node import Node
 File "/var/www/html/scripts/tcviz-1.0/Node.py", line 32
   self.__parent = '1:0' if self.__nodeType == 'class' else None
                          ^
SyntaxError: invalid syntax

Do you have any ideas as to why?

This looks extremely promising.  Thanks Dave:)
(avatar) Vita May 22, MMIX

Hello Dave,

there seem to be two distinct errors:

  1. -bash: dot: command not found means that you don't have Graphviz installed on your system. If you're on Debian or Ubuntu, try apt-get install graphviz to get it. Other distros will surely have the package, too.
  2. The SyntaxError comes from Python and seems to indicate that you have a bit outdated version of Python. The ternary operator that appears on line 32 has been introduced in Python 2.5. Try running python -V (that's uppercase V) to see what version of Python you have.

Hope this helps,

~ Vita

(avatar) Ivan July 29, MMIX
Hey I'm really excited to see this script since it's exactly what I've been looking for.

There's just one MAJOR problem. It won't work in my setup. And I'm very bad at python. In fact I never took the time to learn anything about it.

So, I thought you'd be able to help understand what's wrong from the verbatim copy of ./tcviz.py run that is following right below:

nondescript:~/tcviz/tcviz-1.0# ./tcviz.py ifb0 | dot -Tpng  tc.png
Traceback (most recent call last):
 File "./tcviz.py", line 63, in
   sys.exit(main())
 File "./tcviz.py", line 29, in main
   filters = parse(f, Filter)
 File "./tcviz.py", line 45, in parse
   object = constructor(line)
 File "/root/tcviz/tcviz-1.0/Filter.py", line 23, in __init__
   self.parseSpec(spec)
 File "/root/tcviz/tcviz-1.0/Filter.py", line 28, in parseSpec
   self.__parent = self.__idNormalizer(spec.pop(0))
 File "/root/tcviz/tcviz-1.0/Node.py", line 50, in normalizeId
   return (id if id[-1] != ':' else id + '0')
IndexError: string index out of range

I have "Python 2.5.2 (r252:60911, Jan  4 2009, 17:40:26)"

and

nondescript:~/tcviz/tcviz-1.0# dpkg -l |grep iproute
ii  iproute                            20080725-2

Looking forward to your reply!

Btw BIG thanks for making this script available to everyone!
(avatar) Vita July 29, MMIX

Hi Ivan,

it seems that the script is crashing on some kind of setup that I haven't thought of. Could you please send me the output of these three commands?

  • tc qdisc show dev ifb0
  • tc class show dev ifb0
  • tc filter show dev ifb0

I hope I will be able to fix the script quickly. Thanks!

(avatar) Ivan July 29, MMIX
I sent those to your e-mail address. You should probably answer here so that others could also benefit from your response.
(avatar) Vita July 30, MMIX

Hi,

I've got good news and bad news. The good news is that your TC setup pointed me in the direction of several bugs. These are now fixed in tcviz 1.1 (announcement, project site). The script now generates Graphviz commands correctly.

The bad news is that Graphviz (dot) is choking on a graph of this size (about 190 classes and 280 filters). I tried Graphviz 2.16 and 2.20.2. Version 2.16 just creates a large but completely blank PNG. Version 2.20 complains that the width is too large (60,267px) and then segfaults.

I'm not sure how to tackle this. Maybe Graphviz could be persuaded to stack the nodes vertically as well as horizontally, so that the width wouldn't be getting so huge. However, my knowledge of Graphviz is rather limited. I don't know how to do it or if it is indeed possible...

(avatar) Ivan July 31, MMIX
It seems like there's some kind of patch out there or at least it was said it had been added into graphviz CVS repository. Check this out:  https://mailman.research.att.com/pipermail/graphviz-devel/2008/000750.html
(avatar) Tom McMillan December 23, MMIX
I found a bug, of sorts.

One of my major:minor strings contained hexadecimal numbers:

qdisc ingress ffff: parent ffff:fff1 ----------------

Node.py:setId() choked on this (actually, map() choked on this:
ValueError).

So I hacked the code to do this:

       def hexint(self, value):
               try:
                   x = int(value)
                   return x
               except ValueError:
                   return int(value, 16)

       def setId(self, value):
               (self.__major, self.__minor) = map(self.hexint,
                                                    self.normalizeId(value).s$

You can probably so this more elegantly than I can.  In any case,
thanks for the very useful code.  The pictures are quite nice.

Tom



(avatar) Vita December 28, MMIX

[VIII]

Hello Tom, and thanks for the report! I've (hopefully) fixed the bug in tcviz 1.2 (announcement, project site). Please let me know if you encounter any problems.

(avatar) Tom McMillan December 29, MMIX
Thanks, Vita.  Yes, it works just fine for me.  Thanks for the quick turnaround.  I am very fond of this script, now that I am working
with Linux TC...
(avatar) Charan November 8, MMXI
Hi Friends,

I am unable to show the output for filter in linux. Please help me any one. I am configured below rule.
"tc filter add dev eth0 parent ffff: protocol ip prio 50 u32 match ip dst 0.0.0.1/0 match ip dport 250 0xffff police rate 256kbit burst 10k drop flowid :1"

My doubt is,
Is there any problem in rule ?
or
Is there any problem in my Linux m/c?

Let me know good documents for TC.

Thanks......
(avatar) Dan Siemon October 8, MMXII
Re the Hex bug in an earlier comment... I don't think that solution makes sense. ALL major and minor numbers are in hex.
(avatar) Vita October 8, MMXII
Dan, I think it varies. All systems I've worked with had the numbers in base 10. tcviz nevertheless understands both dec and hex and, if you change the setting in Id.py, can display hex instead of dec.
(avatar) Dan Siemon October 8, MMXII
Pretty sure tc always uses hex.

Even if you are correct and it varies by something, the code in the snippet above won't work because many strings are valid in decimal and hexadecimal so the exception won't be always be raised on systems which use hex.

This is how I found the problem, the graph drawn by tcviz showed several classes merged which sent me down a rat hole trying to find a bug in my script.
(avatar) Vita October 8, MMXII

I've checked out the source of tc and you are right. In the latest tc_util.c the get_tc_classid function used to parse string ids explicitly calls strtoul(..., ..., 16).

So thank you! I've updated Id.py to always assume hexadecimal ids. Does this version work for you?

(avatar) Dan Siemon October 8, MMXII
Yup. That's pretty much exactly what I changed it to.
(avatar) Vita October 10, MMXII
Cool, thanks for your help. I'll release the fixed version once I've got more time.
(avatar) Frankoz June 10, MMXIII
I don't know if this project is still alive... It seems not working with filters at level higher than 1:

I tried to modfy the scripts without success. This is the command used in pyton and parsed by the script:

root@host:~#tc filter show dev eth0
filter parent 1: protocol all pref 49152 u32
filter parent 1: protocol all pref 49152 u32 fh 800: ht divisor 1
filter parent 1: protocol all pref 49152 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1
 match 00000000/00000000 at 0

This instead is the command to list filters at higher level (the "filter show" seems not to be recursive).

root@host:~# tc filter show dev eth0 parent 1:11
filter protocol all pref 1 u32
filter protocol all pref 1 u32 fh 802: ht divisor 1
filter protocol all pref 1 u32 fh 802::800 order 2048 key ht 802 bkt 0 flowid 1:12
 match 00001389/0000ffff at 20
filter protocol all pref 1 u32 fh 802::801 order 2049 key ht 802 bkt 0 flowid 1:12

Note that parent is not in the output, but is the one provided at commandline.
IMHO the script should recur on all nodes to find out possible additional filters.

Thanks and awesome job anyways!!!

Speak your mind

Allowed HTML tags are a, blockquote, em, code, li, ol, p, pre, strong, ul. Links to other comments in the form “[IV]” or “[4]” are detected automatically.