Content-type: text/html
pyshaper.conf - Configuration file format for pyshaper(8)
pyshaper(8) is driven by a configuration file, normally /etc/pyshaper/pyshaper.conf
However, you can override this by setting the environment variable PYSHAPERCONFIG to the full pathname of another file.
The format of pyshaper.conf files is described in the next section.
pyshaper.conf file syntax is simple and straigforward.
Firstly (as is the syntax of many other configuration files), blank lines are ignored, as is all text between the comment character # and the end of the line.
Actual pyshaper.conf declarations are simple one-line statements, as follows:
period seconds
interface.in rate
interface.out rate
interface.class.pri priority
interface.class.out.rate rate
interface.class.out.ceil rate
interface.class.in rate
interface.class.test bool-expression
STATIC SHAPING
There are cases where the traffic you want to shape only needs to be matched against the source host/port, or the destination port. For example, responses from your webserver, or a local p2p app which just happens to be listening on a fixed port.
In such cases, you can fall back on traditional 'static' traffic-shaping practice - that of specifying a combination of remote/local host/port.
In addition to 'dynamic shaping' (periodically scanning current connections and matching them against rules), pyshaper supports 'static shaping'. This means that in traffic class definitions in your pyshaper.conf file, you can specify static properties to match.
To define static matching properties, all you need to do is use one or more of the declarations:
If one or more of these three declarators appears in your configuration, the class will be flagged as 'static', in which tc shaping commands will be executed whether or not there are current connections matching that class.
Also, any connections which match the given raddr/laddr/lport declarations of one or more static classes will not be matched against the rules for any dynamic classes.
Conceptually, 'static' shaping takes precedence over 'dynamic' shaping.
Use of 'static' shaping criteria can have a performance benefit, in that any change in the status of connections which match your static classes will not cause pyshaper to take down and rebuild the current shaping structure. This will avoid the problem of 'transient bandwidth leakage' (Refer pyshaper(1), BUGS).
Let's look at a P2P scenario. Imagine you are running Freenet, and have opened port 13119 to the outside world as your Freenet FNP port. You can create a class which not only matches all Freenet traffic (by testing the connected program's arguments), but also matches traffic to/from port 13119. For this scenario, we could use the class traffic declarations:
1. No specific shaping, just limit eth0 to 128kbits in, 96kbits out:
2. Set eth0 to 256k in/out. Throttle all traffic to France to 40k in, 20-60k out:
Note - the identifier 'france' is not special. the last four lines could be equivalently expressed as:
3. Throttle all I2P connections to 4-16k out (total), and each 3k in:
4. We're running a web server, over a 512 in, 128k out connection. Throttle all default traffic to 4-64kbits, low priority, but give full bandwidth and high priority to replies from our web server:
5. Set up a painful drip-feed for all SMTP connections from Nigeria, assuming that you don't normally correspond with Nigerians. This will delay Nigerian mailservers in their transmissions of 419 scam messages:
STATIC VERSUS DYNAMIC SHAPING
With 'static' rules, pyshaper periodically runs 'netstat' to get a list of current connections, matching these against your rules, then generating and executing tc commands to specifically shape each individual matching connection.
With 'dynamic' rules, pyshaper just runs one set of 'tc' commands to throttle the egress and ingress traffic matching that rule.
One important caveat - if you create a traffic class with both 'static' and 'dynamic' rules, you should look carefully at the ingress (incoming) bandwidth limit. Because if some current connections match the 'static' rule, and other connections match one or more of the 'dynamic' rules, then two pools of ingress bandwidth will be created - one divided up amongst all the connections which match the 'dynamic' rules, and one which gets shared by all the connections matching the 'static' rule.
For instance, if you have a class for I2P traffic, with both static and dynamic rules, and have set the incoming bandwidth to 20kbits/s, then you could find inbound I2P traffic consuming 40kbits/s.
Your options here - (1) halve the inbound limit, and hope that your I2P router has a roughly equal number of incoming and outgoing connections, or (2) don't bother with the static rule - just specify the dynamic rule.
Note, however, that all connections matching a class will share the same outgoing bandwidth pool, regardless of whether they match the static rule or a dynamic rule.
WHAT IS 'tc'?
Info on 'tc' is scarce, so I've gleaned what little I know from the wondershaper script, from the LARTC HOWTO, from the helpful folks on the LARTC mailing list, from the scant info on the tc manpages, and here and there.
tc can be a little hard to fathom, which is why pyshaper was created.
INBOUND VERSUS OUTBOUND
With outbound traffic, tc supports some wonderful constructs like 'Hierarchical Token Bucket' (HTB) that allow you to set up whole trees of bandwidth allocations. You can control your outbound traffic right down to the last byts.
But the mechanism for managing inbound traffic is far more primitive. traffic limits, on inbound traffic matching certain criteria.
Now here's the difference:
- with outbound traffic, you can set up whole 'classes' of traffic, and
set shared bandwidth allocations for any number of connections in a 'class'
- with inbound traffic, you cannot set up such 'pools'. tc can only set up
single policing filters, each with its own separate limit.
To restore some coherency here, what we do is split up the input bandwidth between all connections matching a filter class.
So if we have eth0.someclass.in 128, and there are 4 connections currently matching one or more of the tests for 'eth0.someclass', then pyshaper will set up a policing ingress filter for each connection, and set the rate to 32, with the rate calculated by dividing the total inbound bandwidth allocation by the number of connections which match one or more filters in the class.
BANDWIDTH LEAKAGE
This 'leakage' phenomenon becomes much worse if you're running pyshaper in verbose mode, because instead of piping the tc commands in one hit to a shell subprocess, the commands get printed to stdout and executed, one at a time.
So there is a trade-off between the prompt pickup and shaping of new connections, and leakage of excess bandwidth for existing connections. The best you can do to arrive at the ultimate set up is to experiment.
Generally, the more frequent the shaping cycle, the more you'll need to under-estimate the input and output bandwidth settings.
pyshaper(8)
tc(8)
python(1)