Назад | Перейти на главную страницу

Ограничение / ограничение пропускной способности OpenVPN для каждого пользователя с использованием TC

У меня есть группа пользователей, которые подключаются к моему серверу через OpenVPN TCP и UDP (2 службы). Две службы работают на tun0 и tun1

Я хотел бы иметь возможность ограничить пропускную способность каждого пользователя, чтобы сказать 5 МБ / с вверх и 5 МБ / с вниз с помощью команды TC.

Это было довольно легко реализовать с помощью PPTP, поскольку каждый пользователь получил свой собственный интерфейс, поэтому я мог просто создать новый класс / фильтр для этого интерфейса, ограничив его желаемым пределом скорости, используя что-то вроде этого:

IF=<taken from up script, i.e. ppp1>
tc qdisc del dev $IF root
tc qdisc add dev $IF root handle 1: cbq avpkt 1000 bandwidth 100mbit
tc class add dev $IF parent 1: classid 1:1 cbq rate 10mbit allot 1500 prio 5 bounded isolated
tc filter add dev $IF parent 1: protocol ip prio 16 u32 match ip src 0.0.0.0/0 flowid 1:1
tc qdisc add dev $IF parent 1:1 sfq perturb 10

Насколько я могу судить с OpenVPN, пользователи не имеют собственного интерфейса, весь трафик проходит через основной tun0 и tun1 интерфейсы.

Так что у меня тут 2 проблемы.

1) Похоже, что приведенный выше скрипт не работает с OpenVPN по какой-то причине (установка имени интерфейса на tun0 или tun1) мой тестовый пользователь все еще может загружать файлы на максимальной скорости Интернета.

2) Мне нужно иметь возможность фильтровать это по IP-адресу источника и добавлять его в OpenVPN up скрипт, когда они подключаются, сохраняя при этом другие фильтры / классы, и удаляют этот фильтр / класс в down script, опять же, не влияя на ограничения других подключенных пользователей (т.е. я не могу просто удалять qdisc для tun0 каждый раз, когда пользователь подключается).

Единственная помощь, которую я могу найти при поиске, это

"для этого можно использовать TC"

но без объяснения того, как ...

Спасибо!

Однажды я сделал что-то подобное для индивидуального брандмауэра подключения каждого пользователя. Я реализовал это с помощью learn-address скрипт в OpenVPN, который вызывается, когда пользователь подключается или отключается. Я адаптировал его для вашего случая использования.

Скрипт выглядит следующим образом:

#!/bin/bash

statedir=/tmp/

function bwlimit-enable() {
    ip=$1
    user=$2

    # Disable if already enabled.
    bwlimit-disable $ip

    # Find unique classid.
    if [ -f $statedir/$ip.classid ]; then
        # Reuse this IP's classid
        classid=`cat $statedir/$ip.classid`
    else
        if [ -f $statedir/last_classid ]; then
            classid=`cat $statedir/last_classid`
            classid=$((classid+1))
        else
            classid=1
        fi
        echo $classid > $statedir/last_classid
    fi

    # Find this user's bandwidth limit
    # downrate: from VPN server to the client
    # uprate: from client to the VPN server
    if [ "$user" == "myuser" ]; then
        downrate=10mbit
        uprate=10mbit
    elif [ "$user" == "anotheruser"]; then
        downrate=2mbit
        uprate=2mbit
    else
        downrate=5mbit
        uprate=5mbit
    fi

    # Limit traffic from VPN server to client
    tc class add dev $dev parent 1: classid 1:$classid htb rate $downrate
    tc filter add dev $dev protocol all parent 1:0 prio 1 u32 match ip dst $ip/32 flowid 1:$classid

    # Limit traffic from client to VPN server
    tc filter add dev $dev parent ffff: protocol all prio 1 u32 match ip src $ip/32 police rate $uprate burst 80k drop flowid :$classid

    # Store classid and dev for further use.
    echo $classid > $statedir/$ip.classid
    echo $dev > $statedir/$ip.dev
}

function bwlimit-disable() {
    ip=$1

    if [ ! -f $statedir/$ip.classid ]; then
        return
    fi
    if [ ! -f $statedir/$ip.dev ]; then
        return
    fi

    classid=`cat $statedir/$ip.classid`
    dev=`cat $statedir/$ip.dev`

    tc filter del dev $dev protocol all parent 1:0 prio 1 u32 match ip dst $ip/32
    tc class del dev $dev classid 1:$classid

    tc filter del dev $dev parent ffff: protocol all prio 1 u32 match ip src $ip/32

    # Remove .dev but keep .classid so it can be reused.
    rm $statedir/$ip.dev
}

# Make sure queueing discipline is enabled.
tc qdisc add dev $dev root handle 1: htb 2>/dev/null || /bin/true
tc qdisc add dev $dev handle ffff: ingress 2>/dev/null || /bin/true

case "$1" in
    add|update)
        bwlimit-enable $2 $3
        ;;
    delete)
        bwlimit-disable $2
        ;;
    *)
        echo "$0: unknown operation [$1]" >&2
        exit 1
        ;;
esac

exit 0

Сценарий должен быть установлен в вашем OpenVPN server.conf следующим образом:

learn-address <path-to-script>
script-security 3

script-security 3 необходимо, поэтому OpenVPN фактически вызывает сценарий.

Когда пользователь подключается, сценарий вызывается как <path-to-script> add <ip> <username>, кроме того, сетевой интерфейс помещается в переменную окружения $dev (например. tun0).

Скрипт настраивает сетевой интерфейс для дисциплины очереди и присоединяет к нему необходимые фильтры и классы. Он отслеживает IP-адреса, для которых он установил фильтры и классы, чтобы впоследствии удалить их, если пользователь отключится. Это состояние хранится в каталоге /tmp, это, вероятно, следует изменить.

Обратите внимание, что я не уверен, что получил 100% контроль над трафиком. Формирование трафика загрузки (то есть от OpenVPN к пользователю) работает нормально, но ограничение загрузки не очень точное, что в некоторой степени нормально, насколько я понял. Может быть, вы сможете найти лучший способ и интегрировать его в сценарий.