Limit / Throttle por largura de banda do OpenVPN do usuário usando o TC

5

Eu tenho um grupo de usuários se conectando ao meu servidor via OpenVPN TCP e UDP (2 serviços). Os dois serviços estão operando em tun0 e tun1

Eu gostaria de poder limitar a largura de banda de cada usuário para dizer 5mb / s para cima e 5mb / s para baixo usando o comando TC.

Isso foi bastante fácil de implementar com o PPTP, já que cada usuário tinha sua própria interface, então eu poderia criar uma nova classe / filtro para essa interface limitando-a ao meu limite de velocidade desejado usando algo assim:

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

Tanto quanto eu posso dizer com os usuários do OpenVPN não obtêm sua própria interface, todo o tráfego passa pelas principais interfaces tun0 e tun1 .

Então eu tenho 2 problemas aqui.

1) O script acima parece não funcionar com o OpenVPN por algum motivo (definindo o nome da interface como tun0 ou tun1 ) meu usuário de teste ainda pode fazer o download na velocidade máxima da Internet.

2) Eu preciso poder filtrar esse IP por fonte e adicioná-lo no script up do OpenVPN quando eles se conectam enquanto mantém os outros filtros / classes e remover esse filtro / classe no script down , novamente sem afetando os limites do outro usuário conectado (ou seja, não posso simplesmente excluir o qdisc para tun0 sempre que um usuário se conecta).

A única ajuda que posso encontrar ao pesquisar é

"you can use TC for that"

mas sem explicação de como ...

obrigado!

    
por user1167223 24.06.2015 / 09:39

1 resposta

8

Eu fiz uma vez algo parecido com isso para proteger individualmente a conexão de cada usuário. Eu o implementei usando o script learn-address no OpenVPN, que é chamado quando um usuário se conecta ou desconecta. Eu o adaptei para o seu caso de uso.

O script é o seguinte:

#!/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

O script precisa ser instalado no server.conf do seu OpenVPN da seguinte forma:

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

script-security 3 é necessário para que o OpenVPN chame o script.

Quando um usuário se conecta, o script é chamado de <path-to-script> add <ip> <username> , além disso, a interface de rede é colocada na variável de ambiente $dev (por exemplo, tun0 ).

O script configura a interface de rede para a disciplina de enfileiramento e anexa os filtros e classes necessários a ela. Ele monitora os IPs para os quais instalou filtros e classes, para posteriormente removê-los se o usuário se desconectar. Esse estado é mantido no diretório /tmp , isso provavelmente deve ser alterado.

Note que não tenho certeza de que as coisas do controle de tráfego estão 100% certas. Baixar a modelagem de tráfego (ou seja, do OpenVPN para o usuário) funciona bem, mas limitar o upload não é muito preciso, o que é um pouco normal do que eu entendi. Talvez você possa encontrar uma maneira melhor e integrá-la no script.

    
por 29.06.2015 / 15:19