jablonka.czprosek.czf

freenet-router

Subversion Repositories:
[/] [trunk/] [freenet-router/] [etc/] [firewall/] [qos] - Rev 2

Compare with Previous - Blame - Download


#! /bin/bash
# Firewall nove generace pro Czela Debian 3.0
# Autor: Mirek Slugen
# Spoluatori: Michal Perlik, Michal Vondracek, Jan Chmelensky
# Vytvoreno: 06.11.2006
# Naposledy zmeneno: 08.03.2009
# Tento skript muzete volne sirit a upravovat.

# implementace QoSu
qos_start() {
    # Před každým spuštěním QoSu musíme předchozí QoS ukončit, raději i pokud nebyl aktivní
    qos_stop

    echo "Starting QoS..."

    # lokální proměnné
    local I=""
    local J=""
    local QOS=""
    local DEV=""
    local DEV_2=""
    local RATE=""
    local DUPLEX=""
    local DIRECTION=""
    local COUNT="0"
    local DEVS=""
    local RATES=""
    local DUPLEXS=""
    local DIRECTIONS=""
    local CLASS=""
    local CLASS_DEV=""
    local CLASS_MIN=""
    local CLASS_MAX=""
    local CLASSES=""
    local RATE_MIN=""

    # Zjistime rozhrani, na kterych chceme mit spusteny QoS
    I="0"
    while [ "$I" -lt 20 ]; do
        DEV=DEV${I}_IFACE
        DEV=${!DEV}

        QOS=DEV${I}_QOS
        QOS=${!QOS}

        RATE=DEV${I}_QOS_RATE
        RATE=${!RATE}

        DUPLEX=DEV${I}_QOS_DUPLEX
        DUPLEX=${!DUPLEX}

        DIRECTION=DEV${I}_QOS_DIRECTION
        DIRECTION=${!DIRECTION}

        # inkrementujeme před continue
        ((I++))

        [ "$DEV" == "" ] && continue
        [ "$QOS" != "yes" ] && continue
        [ "$RATE" == "" ] && continue
        [ "$DUPLEX" == "" ] && continue
        [ "$DIRECTION" == "" ] && continue

        DEVS[$COUNT]="$DEV"
        RATES[$COUNT]="$RATE"
        DUPLEXES[$COUNT]="$DUPLEX"
        DIRECTIONS[$COUNT]="$DIRECTION"

        ((COUNT++))
    done

    # skončíme pokud nejsou rozhraní na kterých bychom QoS spustili
    if [ "$COUNT" -lt "1" ]; then
        echo "there is no QoS interface!"
        return 0
    fi

    # Zavedeni modulu pro imq a ifb , v nové verzi už není třeba modul znovu zavádět, v kernelu 2.6.26
    # a vyšším je problém s odstraněním modulu z paměti (rmmod), jednoduchý test problému:
    #   modprobe imq
    #   iptables -t mangle -A PREROUTING -i eth0 -j IMQ --todev 0
    #   ip link set imq0 up
    #   sleep 5
    #   ping www.seznam.cz -c 2
    #   iptables -t mangle -F
    #   ip link set imq0 down
    #   sleep 0.5
    #   rmmod imq
    #
    # Proto zavedeme imq a ifb s maximálním množstvím zařízení abychom ho nemuseli při změně počtu
    # zařízení znovu zavádět. Nezavádíme imq modul, pokud už je zaveden!
    if [ "$QOS_DEVICE" == "imq" ]; then
        [ "`lsmod | awk '{print \$1}' | grep -x \"imq\"`" == "" ] && modprobe imq numdevs=16 &>/dev/null
    elif [ "$QOS_DEVICE" == "ifb" ]; then
        [ "`lsmod | awk '{print \$1}' | grep -x \"ifb\"`" == "" ] && modprobe ifb numifbs=16 &>/dev/null
    fi

    # Nahození příslušních rozhraní, pro fungovaní QoSu je potřeba rozhraní nahodit
    if [ "$QOS_DEVICE" == "imq" ] || [ "$QOS_DEVICE" == "ifb" ]; then
        I="0"
        while [ "$I" -lt "$COUNT" ]; do
            $IP link set ${QOS_DEVICE}$I up
            ((I++))
        done
    fi

    # Na zĂ­skanĂĄ rozhranĂ­ nasadĂ­me QoS
    I="0"
    for DEV in ${DEVS[@]}; do
        RATE="${RATES[$I]}"
        DIRECTION="${DIRECTIONS[$I]}"
        DUPLEX="${DUPLEXES[$I]}"

        # Kontrola na minimĂĄlnĂ­ rychlost rozhranĂ­
        if [ "$RATE" -lt 10 ]; then
            echo "Rate on $DEV is too small!"
            ((I++))
            continue
        fi

        # Pro HTB vypočítáme R2Q
        if [ "$QOS_LIMIT_TYPE" == "htb" ]; then
            if [ "$RATE" -lt 20 ]; then
                echo "Rate on $DEV is too small for HTB R2Q, try set QOS_LIMIT_TYPE to HFSC!"
                R2Q="1"
            elif [ "$RATE" -lt 100 ]; then
                R2Q="1"
            else
                R2Q="$[$RATE/100]"
            fi
        fi

        # speciální garantované třídy
        CLASSES=""
        RATE_MIN="0"
        J="1"
        while true; do
            CLASS_DEV=CLASS_${J}_DEV
            CLASS_DEV=${!CLASS_DEV}

            [ "$CLASS_DEV" == "" ] && break
            if [ "$CLASS_DEV" != "$DEV" ] && [ "$CLASS_DEV" != "all" ]; then
                ((J++))
                continue
            fi

            CLASS_MIN=CLASS_${J}_MIN
            CLASS_MIN=${!CLASS_MIN}

            CLASS_MAX=CLASS_${J}_MAX
            CLASS_MAX=${!CLASS_MAX}

            # garantovaná rychlost nemůže být větší než maximální rychlost na rozhraní
            if [ "$CLASS_MIN" -ge "$RATE" ]; then
                ((J++))
                continue
            fi

            CLASSES=$CLASSES"$J $CLASS_MIN $CLASS_MAX
"

            RATE_MIN=$(($RATE_MIN + $CLASS_MIN))

            ((J++))
        done

        # kontrola na překročení maximální rychlosti rozhraní, potřebujeme alespoň 10 kbitů navíc
        if [ "$(($RATE - $RATE_MIN))" -lt "10" ]; then
            echo "Byla vypoctena pozadovana garantovana rychlost $RATE_MIN, garantovana rychlost na"
            echo "rozhrani $DEV muze byt maximalne $(($RATE - 10)), snizte garantovane rychlosti,"
            echo "nebo nastavte garantovane tridy na konkretni rozhrani, misto rozhrani \"all\"."
            echo ""
            echo "Garantovane tridy pro rozhrani $DEV budou vynechany!"
            echo ""
            CLASSES=""
        fi

        # PomocnĂĄ rozhranĂ­ jako IFB a IMQ
        if [ "$QOS_DEVICE" != "" ]; then
            DEV_2="${QOS_DEVICE}$I"
            echo "    $DEV rate ${RATE} kbit/s with $DEV_2 type $DUPLEX"

            if [ "$QOS_DEVICE" == "imq" ]; then
                if [ "$DUPLEX" == "HD" ]; then
                    if [ "$DUMMY_IFACE" != "" ]; then
                        [ "$DUMMY_IP" == "" ] && DUMMY_IP="`$IP addr show $DUMMY_IFACE | grep inet | grep -v inet6 | awk '{print \$2}' | cut -d \"/\" -f1`"
                        $IPTABLES -t mangle -A POSTROUTING -o $DEV -s ! $DUMMY_IP -j IMQ --todev $I
                    else
                        $IPTABLES -t mangle -A POSTROUTING -o $DEV -j IMQ --todev $I
                    fi
                    $IPTABLES -t mangle -A PREROUTING -i $DEV -j IMQ --todev $I
                    DEV=""
                else
                    $IPTABLES -t mangle -A PREROUTING -i $DEV -j IMQ --todev $I
                fi
            elif [ "$QOS_DEVICE" == "ifb" ]; then
                # IFB neumí poloviční duplex
                [ "$DUPLEX" == "HD" ] && echo "IFB does not support HD (half duplex), using FD (full duplex)!"

                # Kontrola na nåzev rozhraní ath, kterÊ indikuje atheros kartu, ta må problÊmy s IFB
                if [ "${DEV:0:3}" == "ath" ]; then
                    # Test, jestli dojde ke kernel panicu na atheros kartĂĄch:
                    #modprobe ifb
                    #ip link set ifb0 up
                    #tc qdisc add dev ath0 ingress
                    #tc filter add dev ath0 parent ffff: protocol ip prio 10 u32 match u32 0 0 flowid 1:0 action mirred egress redirect dev ifb0
                    echo "IFB on atheros (madwifi) cards could cause kernel panic, try to use IMQ, or"
                    echo "disable QoS on atheros cards!"
                    DEV_2=""
                else
                    $TC qdisc add dev $DEV ingress
                    $TC filter add dev $DEV parent ffff: protocol ip prio 10 u32 match u32 0 0 flowid 1:0 action mirred egress redirect dev $DEV_2
                fi
            fi
        else
            echo "    $DEV rate ${RATE} kbit/s without ifb or imq"
            DEV_2=""
        fi

        # Na zĂĄkladnĂ­m i pomocnĂŠm rozhranĂ­ nasadĂ­me QoS
        for DEV in $DEV $DEV_2; do
            # QoS pro esfq, nebo sfq
            if [ "$QOS_TYPE" == "esfq" ] || [ "$QOS_TYPE" == "layer7_esfq" ]; then
                # Pro ESFQ musíme určit na základě typu rozhraní hash
                if [ "$DIRECTION" == "WAN" ]; then
                    if [ "${DEV:0:3}" != "imq" ] && [ "${DEV:0:3}" != "ifb" ]; then
                        SFQ="esfq perturb 10 hash src"
                    else
                        SFQ="esfq perturb 10 hash dst"
                    fi
                elif [ "$DIRECTION" == "NAT" ]; then
                    if [ "${DEV:0:3}" != "imq" ] && [ "${DEV:0:3}" != "ifb" ]; then
                        SFQ="esfq perturb 10 hash ctnatchg"
                    else
                        SFQ="esfq perturb 10 hash dst"
                    fi
                else
                    if [ "${DEV:0:3}" != "imq" ] && [ "${DEV:0:3}" != "ifb" ]; then
                        SFQ="esfq perturb 10 hash dst"
                    else
                        SFQ="esfq perturb 10 hash src"
                    fi
                fi
            else
                SFQ="sfq perturb 10"
            fi

            # Základní nasazení tříd je pro všechny typy QoSu stejné
            # Vytvoříme root qdisc
            $TC qdisc add dev $DEV root handle 1:0 prio bands 3 priomap 2 2 2 2 2 2 0 0 2 2 2 2 2 2 2 2

            # V prvních dvou třídách nebudeme limitovat, pouze budeme rozdělovat pásmo pomocí sfq
            $TC qdisc add dev $DEV parent 1:1 handle 11:0 $SFQ
            $TC qdisc add dev $DEV parent 1:2 handle 12:0 $SFQ

            # Omarkované pakety roztřídíme do jednotlivých tříd, první třída bude mít prioritu 1,
            # tj. cokoliv co do ní přejde už nezávisle na dalších pravidlech zpracuje, ostatní
            # nemají prioritu udanou, tím se řadí na konec až za všechna ostatní pravidla.
            $TC filter add dev $DEV parent 1:0 protocol ip prio 1 handle 1 fw flowid 1:1
            $TC filter add dev $DEV parent 1:0 protocol ip handle 2 fw flowid 1:2

            # 3. třída je určena pro datového omezení
            if [ "$QOS_LIMIT_TYPE" == "htb" ]; then
                $TC qdisc add dev $DEV parent 1:3 handle 13:0 htb r2q $R2Q default 111

                # Zakladni htb tride dame plnou rychlost, dalsi budou mit rychlost sdilenou HTTP,mail, DC++,
                $TC class add dev $DEV parent 13:0 classid 13:1 htb rate ${RATE}kbit
            else
                $TC qdisc add dev $DEV parent 1:3 handle 13:0 hfsc default 111

                # Zakladni hfsc tride dame plnou rychlost, dalsi budou mit rychlost sdilenou HTTP,mail, DC++,
                $TC class add dev $DEV parent 13:0 classid 13:1 hfsc ls m2 ${RATE}kbit ul m2 ${RATE}kbit
            fi

            # Pokročilé nasazení tříd podle typu
            if [ "$QOS_TYPE" == "layer7" ] || [ "$QOS_TYPE" == "layer7_esfq" ]; then
                if [ "$QOS_LIMIT_TYPE" == "htb" ]; then
                    $TC class add dev $DEV parent 13:1 classid 13:111 htb rate $[5*($RATE - $RATE_MIN)/10]kbit ceil ${RATE}kbit
                    $TC class add dev $DEV parent 13:1 classid 13:112 htb rate $[3*($RATE - $RATE_MIN)/10]kbit ceil ${RATE}kbit
                    $TC class add dev $DEV parent 13:1 classid 13:113 htb rate $[2*($RATE - $RATE_MIN)/10]kbit ceil ${RATE}kbit
                else
                    $TC class add dev $DEV parent 13:1 classid 13:111 hfsc ls m2 $[5*($RATE - $RATE_MIN)/10]kbit ul m2 ${RATE}kbit
                    $TC class add dev $DEV parent 13:1 classid 13:112 hfsc ls m2 $[3*($RATE - $RATE_MIN)/10]kbit ul m2 ${RATE}kbit
                    $TC class add dev $DEV parent 13:1 classid 13:113 hfsc ls m2 $[2*($RATE - $RATE_MIN)/10]kbit ul m2 ${RATE}kbit
                fi

                # V kazde tride jeste pouziji sfq
                $TC qdisc add dev $DEV parent 13:111 handle 111:0 $SFQ
                $TC qdisc add dev $DEV parent 13:112 handle 112:0 $SFQ
                $TC qdisc add dev $DEV parent 13:113 handle 113:0 $SFQ

                # Další třídy zpracovávající pravidla z iptables
                $TC filter add dev $DEV parent 13:0 protocol ip handle 3 fw flowid 13:111
                $TC filter add dev $DEV parent 13:0 protocol ip handle 4 fw flowid 13:112
                $TC filter add dev $DEV parent 13:0 protocol ip handle 5 fw flowid 13:113
            else
                # Nastavime maximalni ryhlost pro vse co jde do tridy 2 (3. skupina)
                if [ "$QOS_LIMIT_TYPE" == "htb" ]; then
                    $TC class add dev $DEV parent 13:1 classid 13:111 htb rate $[($RATE - $RATE_MIN)]kbit ceil ${RATE}kbit
                else
                    # Zakladni hfsc tride dame plnou rychlost
                    $TC class add dev $DEV parent 13:1 classid 13:111 hfsc ls m2 $[($RATE - $RATE_MIN)]kbit ul m2 ${RATE}kbit
                fi

                # V kazde tride jeste pouziji sfq
                $TC qdisc add dev $DEV parent 13:111 handle 111:0 $SFQ

                # I pokud nepouŞívåme layer7, můŞeme vyuŞít markovåní pomocí iptables
                $TC filter add dev $DEV parent 13:0 protocol ip handle 3 fw flowid 13:111
            fi

            # speciální třídy
            IFS=$'\t\n'
            for CLASS in $CLASSES; do
                IFS=$' \t\n'
                CLASS=( $CLASS )
                # garantovaná rychlost nemůže být větší než maximální rychlost na rozhraní
                [ "${CLASS[1]}" -ge "$RATE" ] && continue
                # pokud nenĂ­ zadanĂĄ maximĂĄlnĂ­ rychlost, tak je maximum celkovĂ˝ rychlost rozhranĂ­
                [ "${CLASS[2]}" == "" ] && CLASS[2]=$RATE
                # pokud je maximĂĄlnĂ­ rychlost menĹĄĂ­ neĹž minimĂĄlnĂ­, tak je maximum rychlost rozhranĂ­
                [ "${CLASS[2]}" -lt "${CLASS[1]}" ] && CLASS[2]=$RATE
                # pokud je maximální rychlost větší, než maximální rychlost rozhraní, tak je maximum rychlost rozhraní
                [ "${CLASS[2]}" -gt "$RATE" ] && CLASS[2]=$RATE

                if [ "$QOS_LIMIT_TYPE" == "htb" ]; then
                    $TC class add dev $DEV parent 13:1 classid 13:$((113 + ${CLASS[0]})) htb rate ${CLASS[1]}kbit ceil ${CLASS[2]}kbit
                else
                    $TC class add dev $DEV parent 13:1 classid 13:$((113 + ${CLASS[0]})) hfsc ls m2 ${CLASS[1]}kbit ul m2 ${CLASS[2]}kbit
                fi

                # Nasadíme sfq na dané třídy
                $TC qdisc add dev $DEV parent 13:$((113 + ${CLASS[0]})) handle $((113 + ${CLASS[0]})):0 $SFQ

                # Garantované třídy nepotřebují speciální třídu, protože se markují jen pomocí tc rovnou do dané třídy
                #$TC filter add dev $DEV parent 1:0 protocol ip handle $((5 + ${CLASS[0]})) fw flowid 13:$((113 + ${CLASS[0]}))
                #$TC filter add dev $DEV parent 13:0 protocol ip handle $((5 + ${CLASS[0]})) fw flowid 13:$((113 + ${CLASS[0]}))
            done
            IFS=$' \t\n'
        done
        ((I++))
    done

    # Protože markování na každém zařízení zvlášť by bylo velmi mnoho pravidel pro iptables,
    # tak použijeme markování na všech zařízeních, i tak zavedení pravidel trvá cca 6 vteřin.
    if [ "$QOS_TYPE" == "layer7" ] || [ "$QOS_TYPE" == "layer7_esfq" ]; then
        # Markování musíme provádět již v preroutingu, jinak nejsme schopni označit všechny pakety.
        # ICMP - vzdy
        $IPTABLES -t mangle -I PREROUTING -p icmp -j MARK --set-mark 1
        # OSPF - vzdy
        $IPTABLES -t mangle -I PREROUTING -p ospf -j MARK --set-mark 1
        # UDP - vzdy
        $IPTABLES -t mangle -I PREROUTING -p UDP -j MARK --set-mark 2
        # HTML - jiny nekorektni zpusob markovani http, ale mnohem mene narocny
        #$IPTABLES -t mangle -I PREROUTING -p TCP --dport 80 -j MARK --set-mark 3

        # Označení paketů pomocí layer7 filtru, detekce některých protokolů zvyšuje odezvy
        # 8.3.2009 - vyřazeny protokoly, které po cca 30 dnech měly nulové čítače
        qos_mark_layer7 1 "bgp dhcp dns irc jabber snmp whois"
        qos_mark_layer7 2 "quake-halflife worldofwarcraft"
        # Do třídy 3 jdou všechna nezařazená data, takže jí není třeba označovat
        qos_mark_layer7 3 ""
        qos_mark_layer7 4 "ftp cvs biff h323 imap live365 pop3 rtsp shoutcast smtp ssl tftp"
        qos_mark_layer7 5 "bittorrent directconnect edonkey http-itunes soulseek"
    fi

    echo "done."

    # Označíme jednotlivé počítače v třídách
    qos_guaranted_classes
}

qos_stop() {
    echo -n "Stopping QoS..."

    local I=""
    local DEV=""

    # SmaĹžeme vĹĄechna pravidla v iptables
    for I in `$IPTABLES -t mangle -L POSTROUTING -n -v --line-numbers | grep "set 0x" | awk '{print $1}' | sort -r -n`; do
        $IPTABLES -t mangle -D POSTROUTING $I
    done

    for I in `$IPTABLES -t mangle -L PREROUTING -n -v --line-numbers | grep "set 0x" | awk '{print $1}' | sort -r -n`; do
        $IPTABLES -t mangle -D PREROUTING $I
    done

    for I in `$IPTABLES -t mangle -L POSTROUTING -n -v --line-numbers | grep "todev" | awk '{print $1}' | sort -r -n`; do
        $IPTABLES -t mangle -D POSTROUTING $I
    done

    for I in `$IPTABLES -t mangle -L PREROUTING -n -v --line-numbers | grep "todev" | awk '{print $1}' | sort -r -n`; do
        $IPTABLES -t mangle -D PREROUTING $I
    done

    # SmaĹžeme vĹĄechny root qdisky
    while true; do
        DEV="`$TC qdisc | grep -v 0: | awk '{print \$5}' | sort -u | sed -n 1p`"
        [ "$DEV" == "" ] && break
        $TC qdisc del dev "$DEV" root &>/dev/null
        $TC qdisc del dev "$DEV" ingress &>/dev/null
    done

    # Deaktivujeme všechna ifb zařízení
    while true; do
        DEV="`$IP link show | grep ifb | grep UP | awk '{print \$2}' | sort -u | sed -n 1p`"
        [ "$DEV" == "" ] && break
        DEV="${DEV//:/}"
        $IP link set "$DEV" down
    done

    # Odstraníme ifb modul z paměti
    #rmmod -f ifb &>/dev/null

    # Deaktivujeme všechna imq zařízení
    while true; do
        DEV="`$IP link show | grep imq | grep UP | awk '{print \$2}' | sort -u | sed -n 1p`"
        [ "$DEV" == "" ] && break
        DEV="${DEV//:/}"
        $IP link set "$DEV" down
    done

    # Odstraníme imq modul z paměti
    #rmmod -f imq &>/dev/null

    echo "done."
}

qos_stats() {
    $TC -s qdisc
}

# speciální garantované třídy
qos_guaranted_classes() {
    [ "${#CLASS_1[*]}" -lt "1" ] && return 0

    echo "Starting QoS guaranted classes..."

    # lokální proměnné
    local I=""
    local J=""
    local CLASS_DEV=""
    local CLASS_MIN=""
    local CLASS_MAX=""
    local CLASS_USERS=""
    local CLASSES=""
    local RATE_MIN="0"
    local USER=""

    J="1"
    while true; do
        CLASS_DEV=CLASS_${J}_DEV
        CLASS_DEV=${!CLASS_DEV}

        [ "$CLASS_DEV" == "" ] && break

        CLASS_MIN=CLASS_${J}_MIN
        CLASS_MIN=${!CLASS_MIN}

        CLASS_MAX=CLASS_${J}_MAX
        CLASS_MAX=${!CLASS_MAX}

        # klasické přiřazení nelze použít na pole
        CLASS_USERS="CLASS_${J}[@]"
        CLASS_USERS=( "${!CLASS_USERS}" )

        # Přeskočíme prázdné třídy
        if [ "${#CLASS_USERS[*]}" -lt "1" ]; then
            ((J++))
            continue
        fi

        # Pokud není zadána maximální rychlost třídy
        [ "$CLASS_MAX" == "" ] && CLASS_MAX="interface maximum"

        echo "QoS class $J (dev: $CLASS_DEV, min: $CLASS_MIN kbit/s, max: $CLASS_MAX kbit/s):"

        IFS=$'\t\n'
        for USER in ${CLASS_USERS[@]}; do
            IFS=$' \t\n'
            USER=( $USER )
            I="2"
            while [ "${USER[$I]}" != "" ]; do
                if [ "${USER[$I]:2:1}" == ":" ]; then
                    echo -e "    user ${USER[1]}\twith mac ${USER[$I]}\ton ${USER[0]}\tlimited!"
                else
                    echo -e "    user ${USER[1]}\twith ip  ${USER[$I]}\t\ton ${USER[0]}\tlimited!"
                fi
                qos_guaranted_class_add_user "$J" "${USER[$I]}" "${USER[0]}"
                ((I++))
            done
        done
        IFS=$' \t\n'

        ((J++))
    done

    echo "done."
}

# formát: "třída" "ip, nebo mac" "rozhraní - nepovinné"
qos_guaranted_class_add_user() {
    ( [ "$1" == "" ] || [ "$2" == "" ] ) && return 0

    # rozhraní na kterých je aktivní qos, vŞdy pouŞívåme alespoň sfq
    local TC_DEVS=`$TC qdisc | grep sfq | awk '{print $5}' | sort -u`

    # další lokální proměnné
    local I=""
    local DEV=""
    local TC_DEV=""
    local TC_DEVS_POM=""
    local IMQ_DEV=""
    local IFB_DEV=""
    local DATA=""
    local LINE=""
    local IP="$2"
    local MAC="`echo $2 | tr [:upper:] [:lower:]`"
    local TC_NUM=""
    local CLASS=""

    # pokud ip, nebo mac uĹž je zadanĂĄ, tak jĂ­ smaĹžeme na vĹĄech rozhranĂ­ch
    qos_guaranted_class_del_user "$2"

    # zjistĂ­me jestli jde o MAC, nebo IP adresu
    IFS=$'./\t\n'
    IP=( $IP )
    IFS=$':\t\n'
    MAC=( $MAC )
    IFS=$' \t\n'

    # Je-li zadané rozhraní, tak musíme najít přidružené IMQ, nebo IFB
    if [ "$3" != "" ] && [ "$3" != "all" ]; then
        # IMQ z PREROUTINGu, musíme použít awk, protože iptables posílá * a ta se nedá převést do pole!
        DATA=`$IPTABLES -t mangle -L PREROUTING -n -v | grep todev | awk '{print $6" "$12}'`

        IFS=$'\t\n'
        for LINE in $DATA; do
            IFS=$' \t\n'
            LINE=( ${LINE} )
            if [ "${LINE[0]}" == "$3" ] && [ "${LINE[1]}" != "" ]; then
                IMQ_DEV="imq${LINE[1]}"
                break
            fi
        done
        IFS=$' \t\n'

        # ifb zĂ­skĂĄme z tc
        IFB_DEV="`$TC filter list dev $3 parent ffff: | grep ifb | awk '{print \$9}'`"
        IFB_DEV="${IFB_DEV//)/}"

        for I in $TC_DEVS; do
            [ "$3" == "$I" ] && TC_DEVS_POM=$TC_DEVS_POM" $I"
            [ "$IFB_DEV" != "" ] && [ "$IFB_DEV" == "$I" ] && TC_DEVS_POM=$TC_DEVS_POM" $IFB_DEV"
            [ "$IMQ_DEV" != "" ] && [ "$IMQ_DEV" == "$I" ] && TC_DEVS_POM=$TC_DEVS_POM" $IMQ_DEV"
        done

        [ "$TC_DEVS_POM" == "" ] && return 0

        # MusĂ­me odstranit prvnĂ­ prĂĄzdnĂ˝ znak
        TC_DEVS="${TC_DEVS_POM:1}"
    fi

    # samotné zařazení do třídy
    for DEV in $TC_DEVS; do
        # smažeme nejprve třídy 1:0 a pak 13:0
        for CLASS in "1:0" "13:0"; do
            # musíme najít další číslo kam budeme přidávat záznam
            TC_NUM=$((`$TC filter list dev $DEV parent $CLASS | awk '{print $5}' | grep -v "4915" | sort -ug | tail -n 1` + 1))

            if [ "${IP[3]}" != "" ]; then
                $TC filter add dev $DEV parent $CLASS protocol ip prio $TC_NUM u32 match ip src $2 flowid 13:$((113 + $1))
                $TC filter add dev $DEV parent $CLASS protocol ip prio $TC_NUM u32 match ip dst $2 flowid 13:$((113 + $1))
            elif [ "${MAC[5]}" != "" ]; then
                $TC filter add dev $DEV parent $CLASS protocol ip prio $TC_NUM u32 match u16 0x0800 0xffff at -2 match u32 0x${MAC[2]}${MAC[3]}${MAC[4]}${MAC[5]} 0xffffffff at -12 match u16 0x${MAC[0]}${MAC[1]} 0xffff at -14 flowid 13:$((113 + $1))
                $TC filter add dev $DEV parent $CLASS protocol ip prio $TC_NUM u32 match u16 0x0800 0xffff at -2 match u16 0x${MAC[4]}${MAC[5]} 0xffff at -4 match u32 0x${MAC[0]}${MAC[1]}${MAC[2]}${MAC[3]} 0xffffffff at -8 flowid 13:$((113 + $1))
            else
                # nejednĂĄ se ani o ip ani o mac adresu
                return 0
            fi
        done
    done

    return 0
}

# Pro mazání není třeba znát třídu ze které mažeme, protože by jedna ip, nebo mac
# neměla být ve více třídách.
#
# formĂĄt: "ip, nebo mac"
qos_guaranted_class_del_user() {
    [ "$1" == "" ] && return 0

    # MaĹžeme na vĹĄech rozhranĂ­ch, kde je QoS
    local TC_DEVS=`$TC qdisc | grep sfq | awk '{print $5}' | sort -u`

    # lokální proměnné
    local I=""
    local DEV=""
    local DATA=""
    local LINE=""
    local LINE_1=""
    local LINE_2=""
    local IP="$1"
    local MAC="`echo $1 | tr [:upper:] [:lower:]`"
    local IP_HEX=""
    local MAC_1=""
    local MAC_2=""
    local MAC_3=""
    local CLASS=""
    local PRIO=""
    local PRIO_PREV=""

    # zjistíme jestli jde o MAC, nebo IP adresu, pro mac musíme převést velká písmena na malá
    IFS=$'./\t\n'
    IP=( $IP )
    IFS=$':\t\n'
    MAC=( $MAC )
    IFS=$' \t\n'

    if [ "${IP[3]}" != "" ]; then
        # převedeme ip do formátu, který používá tc
        IP_HEX=`printf '%02x%02x%02x%02x\n' ${IP[0]} ${IP[1]} ${IP[2]} ${IP[3]}`
    elif [ "${MAC[5]}" != "" ]; then
        MAC_1="00000800"
        MAC_2="${MAC[2]}${MAC[3]}${MAC[4]}${MAC[5]}"
        MAC_3="0000${MAC[0]}${MAC[1]}"
    else
        # NejednĂĄ se ani o ip ani o mac adresu
        return 0
    fi

    # zkontrolujeme vĹĄechna rozhranĂ­
    for DEV in $TC_DEVS; do
        # smažeme nejprve třídy 1:0 a pak 13:0
        for CLASS in "1:0" "13:0"; do
            # načteme data z tc, kde jsou uloženy všechny zadané mac i ip adresy
            IFS=$'\t\n'
            DATA=( `$TC filter list dev $DEV parent $CLASS` )
            IFS=$' \t\n'

            I="0"
            PRIO=""
            PRIO_PREV=""

            IFS=$'\t\n'
            for LINE in ${DATA[@]}; do
                # pro matchování nás nezajímá maska, proto masku oddělíme, platí jen v případě ip
                IFS=$' /\t\n'
                LINE=( $LINE )
                IFS=$' \t\n'

                # inkrementovat musíme ještě před continue
                ((I++))

                if [ "${LINE[3]}" == "pref" ]; then
                    PRIO=${LINE[4]}
                    continue
                fi
                # dĂĄle nĂĄs zajĂ­mĂĄ jen match
                [ "${LINE[0]}" != "match" ] && continue
                # pokud neznáme prioritu, tak pokračujeme
                [ "$PRIO" == "" ] && continue
                # pokud jsme již jednou tuto prioritu mazali, tak pokračujeme
                [ "$PRIO" == "$PRIO_PREV" ] && continue

                # rozdělení podle ip, nebo mac
                if [ "${IP[3]}" != "" ]; then
                    # pokud ip neodpovídá, tak pokračujeme
                    [ "${LINE[1]}" != "$IP_HEX" ] && continue
                elif [ "${MAC[5]}" != "" ]; then
                    # jednotnĂ˝ match pro vĹĄechny mac adresy
                    [ "${LINE[1]}/${LINE[2]}" != "$MAC_1/0000ffff" ] && continue
                    # následující řádek musí obsahovat druhou část mac adresy, už nepoužíváme jako delimiter /
                    LINE_1=( ${DATA[$I]} )
                    [ "${LINE_1[1]}" != "$MAC_2/ffffffff" ] && continue
                    # další řádek musí obsahovat poslední část mac adresy
                    LINE_2=( ${DATA[$(($I + 1))]} )
                    [ "${LINE_2[1]}" != "$MAC_3/0000ffff" ] && continue
                fi

                # DanĂĄ ip uĹž je matchovanĂĄ, proto pravidla smaĹžeme
                $TC filter del dev $DEV parent $CLASS protocol ip prio $PRIO 2>/dev/null

                PRIO_PREV="$PRIO"
            done
            IFS=$' \t\n'
        done
    done

    return 0
}

qos_mark_layer7() {
    ( [ "$1" == "" ] || [ "$2" == "" ] ) && return 0

    # Lokální proměnné
    local I=""
    local O_DEV=""
    local I_DEV=""

    [ "$3" != "" ] && O_DEV="-o $3"
    [ "$4" != "" ] && I_DEV="-i $4"

    for I in $2; do
        $IPTABLES -t mangle -I PREROUTING $O_DEV $I_DEV -m layer7 --l7proto $I -j MARK --set-mark $1
    done

    return 0
}

Powered by WebSVN 2.2.1