Friday, January 28, 2011

How can I do traffic shaping in Linux by IP?

We have a transparent proxy setup. I tried looking for traffic shaping in Linux, and all I could find online was to limit traffic by interface (eth0/eth1...).

I need to limit the bandwidth (never exceeding a specific limit) by IP address or IP ranges and I can't find a way to do that.

Is there any way to do that?

  • Something like this worked for me to limit a contractor's web cam to a limited amount of bandwidth. Check out the man page for tc for details.

    #!/bin/bash
    set -x
    
    DEV=eth0
    export DEV
    
    tc qdisc del dev $DEV root
    tc qdisc del dev $DEV root
    tc qdisc add dev $DEV root handle 1: cbq avpkt 1000 bandwidth 100mbit
    
    # setup a class to limit to 1500 kilobits/s
    tc class add dev $DEV parent 1: classid 1:1 cbq rate 1500kbit \
       allot 1500 prio 5 bounded isolated
    
    # add traffic from 10.2.1.37 to that class
    tc filter add dev $DEV parent 1: protocol ip prio 16 u32 \
       match ip src 10.2.1.37 flowid 1:1
    
    Julien Vehent : CBQ is a bit abandonned... you would find HTB much easier to use and obtain the same result
    From Zoredache
  • I'm not sure I understand your question correctly.

    Transparent proxying (as in Squid for HTTP) is used to control mostly incoming data. While traffic shaping is used to control outgoing data.

    You need to provide more details. If you have a lot of workstations behind a HTTP proxy and are trying to limit their download speeds, you'd better go for something like Squid + delay pools.

    From halp
  • The traffic shaping layer of the kernel is, basically, a packet scheduler attached to your network card. So one traffic shaping policy applies to one network card.

    What you can do, in your case, is to create a list of IP and bandwidth attached, and then, for each IP, you create:

    • One traffic shaping rule identified by a classid
    • One netfilter rule that will mark packets to a specific mark value
    • One Filter that will bind that packets marks to the classid, thus applying the traffic control rule to the specified packets.

    The example given by @Zoredache works, but I personnally prefer to use Netfilter capability instead of TC to filter packets, and HTB instead of CBQ for the shapping algorithm. So you can try something like this (requires Bash 4 for associative arrays):

    #! /bin/bash
    NETCARD=eth0
    MAXBANDWIDTH=100000
    
    # reinit
    tc qdisc del dev $NETCARD root handle 1
    tc qdisc add dev $NETCARD root handle 1: htb default 9999
    
    # create the default class
    tc class add dev $NETCARD parent 1:0 classid 1:9999 htb rate $(( $MAXBANDWIDTH ))kbit ceil $(( $MAXBANDWIDTH ))kbit burst 5k prio 9999
    
    # control bandwidth per IP
    declare -A ipctrl
    # define list of IP and bandwidth (in kilo bits per seconds) below
    ipctrl[192.168.1.1]="256"
    ipctrl[192.168.1.2]="128"
    ipctrl[192.168.1.3]="512"
    ipctrl[192.168.1.4]="32"
    
    mark=0
    for ip in "${!ipctrl[@]}"
    do
        mark=$(( mark + 1 ))
        bandwidth=${ipctrl[$ip]}
    
        # traffic shaping rule
        tc class add dev $NETCARD parent 1:0 classid 1:$mark htb rate $(( $bandwidth ))kbit ceil $(( $bandwidth ))kbit burst 5k prio $mark
    
        # netfilter packet marking rule
        iptables -t mangle -A INPUT -i $NETCARD -s $ip -j CONNMARK --set-mark $mark
    
        # filter that bind the two
        tc filter add dev $NETCARD parent 1:0 protocol ip prio $mark handle $mark fw flowid 1:$mark
    
        echo "IP $ip is attached to mark $mark and limited to $bandwidth kbps"
    done
    
    #propagate netfilter marks on connections
    iptables -t mangle -A POSTROUTING -j CONNMARK --restore-mark
    

    -- edit: forgot the default class and to propagate marks at the end of the script.

0 comments:

Post a Comment