3

I'd like to automatically shape bandwidth-hungry TCP connections corresponding to the already transmitted data.

A summary would be : "The first Mbit is transferred at full bandwidth, then it gradually drops to 1% of the bandwidth when it reaches 10Mbit, and stays there".

An example is better than 1k words :

 b-w  ^
      |        .         .
100%  | ********         .
      |        . *       .
      |        .    *    .
      |        .       * .
  1%  |        .         .********
      +----------------------------> Data transfered.
         1Mbit .         . 10Mbit

I know that a full traffic shaping would be better, since it would allow bursting and use of leftover bandwidth, but the idea is specifically to automatically limit big data transfers without any further configuration.

How would I implement this on a linux host ?

Update: It isn't immediately obvious, but the data meter counts both ways (upload & download) as I purposefully didn't precise download or upload.

2
  • 2
    You forgot to ask a question. You also forgot to tell us how you wanted to do this. (In a firewall? On a host? In a router?) Dec 19, 2011 at 13:09
  • @DavidSchwartz thx. Updated the question. Sorry. Dec 19, 2011 at 13:22

2 Answers 2

3

The HTB qdisc implements the concept of bursts which is a bit of what you want - it sends at full hardware rate up to the amount of data specified in the "burst" parameter. To get a gradual decrease you would need to nest HTB classes, and probably you would not want to do it too excessively as it greatly increases the complexity of the setup. But the Linux traffic shaping engine itself is stateless, it just acts upon packets, not connections. Using tc filters alone, you can only differentiate packets based on IP/TCP headers.

So if you need to classify differently based on connections, the most straightforward approach would probably be using the iptables "--connbytes" match and a packet mark (-j MARK target) to shove a connection's packets into the right queue (fast/slowdown)

See the wordy section on bandwidth management of the LARTC howto and / or the comprehensive "Open source bandwidth solutions" whitepaper for more insight.

Furthermore, if you need to shape upstream as well as downstream, take a look at the implementation of the virtual IMQ interface - it has been designed specifically for this purpose.

9
  • How would I automatically reclassify the TCP connection to another class after a certain amount of data was transfered ? Dec 19, 2011 at 16:03
  • @SteveSchnepp I think there's an iptables match rule that can measure the byte offset into a TCP stream.
    – Alnitak
    Dec 19, 2011 at 16:15
  • 1
    I think the most straightforward way would be using the iptables "--connbytes" match and a packet mark (-j MARK target) to shove a connection's packets into the right queue (fast/slowdown) if you need a per-connection-approach. I forgot to mention a paper on the topic of bandwidth control - "Open source bandwidth solutions" is worth reading if you are not yet familiar with the topic.
    – the-wabbit
    Dec 19, 2011 at 16:28
  • 1
    @SteveSchnepp no, I was thinking of the --connbytes option that @syneticon-dj just mentioned.
    – Alnitak
    Dec 19, 2011 at 16:29
  • 1
    BTW: as per your edit, if you need to shape upstream as well as downstream, take a look at the implementation of the virtual IMQ interface - it has been designed specifically for this purpose.
    – the-wabbit
    Dec 19, 2011 at 16:31
1

I was inspired by @the-wabbit solution to this, and managed to solve this. I blogged about this solution in my personal wiki: https://giki.wiki/@nubela/Software-Engineering/Per-Connection-Throttling

Here's my solution to this question with an actual shell script:

#!/bin/sh

dev=eth0
ip_port=3002
rate_limit=512kbit
rate_ceil=1024kbit
htb_class=10
max_byte=10485760

if [ "$(id -u)" != "0" ]; then
    echo "This script must be run as root" 1>&2
    exit 1
fi

if [ "$1" = "enable" ]; then
    echo "enabling rate limits"
    tc qdisc del dev $dev root > /dev/null 2>&1
    tc qdisc add dev $dev root handle 1: htb

    tc class add dev $dev parent 1: classid 1:$htb_class htb rate $rate_limit ceil $rate_ceil
    tc filter add dev $dev parent 1: prio 0 protocol ip handle $htb_class fw flowid 1:$htb_class

    #iptables -t mangle -A OUTPUT -p tcp --sport $ip_port -j MARK --set-mark $htb_class

    # small packet is probably interactive or flow control
    iptables -t mangle -A OUTPUT -p tcp --sport $ip_port -m length --length 0:500 -j RETURN

    # small packet connections: multi purpose (don't harm since not maxed out)
    iptables -t mangle -A OUTPUT -p tcp --sport $ip_port -m connbytes --connbytes 0:250 --connbytes-dir both --connbytes-mode avgpkt -j RETURN

    #after 10 megabyte a connection is considered a download
    iptables -t mangle -A OUTPUT -p tcp --sport $ip_port -m connbytes --connbytes $max_byte: --connbytes-dir both --connbytes-mode bytes -j MARK --set-mark $htb_class
    iptables -t mangle -A OUTPUT -j RETURN

elif [ "$1" = "disable" ]; then
    echo "disabling rate limits"
    tc qdisc del dev $dev root > /dev/null 2>&1

    iptables -t mangle -F
    iptables -t mangle -X

elif [ "$1" = "show" ]; then
    tc qdisc show dev $dev
    tc class show dev $dev
    tc filter show dev $dev
    iptables -t mangle -vnL INPUT
    iptables -t mangle -vnL OUTPUT
else
    echo "invalid arg $1"
fi

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .