24/05/2010

Eeegw – part III – Quality Of Service aka QOS


Voici le script de QOS final basé sur CTB et PRIO (celui que j’utilise en production)

#!/bin/bash
### Script de reservation de bande passante par bragon

## On fixe quelques variables

EXTERNAL_DOWNLINK_KBPS="6400" 
## je sais downloader à  ~800ko/s ma limite est donc a 6400 Kbps
EXTERNAL_DL_IPSEC_KBPS="1024"
EXTERNAL_DL_PPTP_KBPS=""
EXTERNAL_DL_ICA_KBPS=""
EXTERNAL_DL_TSE_KBPS="128" 
EXTERNAL_DL_SMTP_KBPS="512" 
EXTERNAL_DL_HTTP_KBPS="1024" 
EXTERNAL_DL_OTHERTCP_KBPS="2048" 
EXTERNAL_DL_OTHERTCP_PORT="2048" 
## je sais uploader à environ 80Ko/s je fixe donc ma limite a 512 afin d'être sûr de ne pas engendrer de lag.
EXTERNAL_UPLINK_KBPS="512" 
EXTERNAL_UL_IPSEC_KBPS="" 
EXTERNAL_UL_IPSEC_SHARE="" 
EXTERNAL_UL_PPTP_KBPS="" 
EXTERNAL_UL_PPTP_SHARE="" 
EXTERNAL_UL_ICA_KBPS="" 
EXTERNAL_UL_ICA_SHARE="" 
EXTERNAL_UL_TSE_KBPS="512" 
EXTERNAL_UL_TSE_SHARE="512" 
EXTERNAL_UL_SMTP_KBPS="256" 
EXTERNAL_UL_SMTP_SHARE="256"
EXTERNAL_UL_HTTP_KBPS="32" 
EXTERNAL_UL_HTTP_SHARE="" 
EXTERNAL_UL_OTHERTCP_KBPS="" 
EXTERNAL_UL_OTHERTCP_SHARE="" 
EXTERNAL_UL_OTHERTCP_PORT="" 
EXTERNAL_UL_VOIP_KBPS="256" 
EXTERNAL_UL_VOIP_SHARE="isolated" 
EXTERNAL_DEV_BANDWIDTH="" 

## CE script de QOS s'applique à toutes les interface dans MON cas cela convient.
## Je vous conseille de retirer votre interface LAN du match si vous avez plusieurs interfaces reseau.
## La commande va permettre de lister toutes les interfaces et d'appliquer les regles de QOS dessus.
## On match aussi les interfaces tap

if [ -n "$EXTERNAL_DOWNLINK_KBPS" ] && [ -n "$EXTERNAL_UPLINK_KBPS" ] && echo $INTERFACE | egrep -q "(eth[023]|ppp[0-9]|ippp0|tap[0-3])" ; then

#In kilobits
DOWNLINK=$EXTERNAL_DOWNLINK_KBPS
UPLINK=$EXTERNAL_UPLINK_KBPS
BNDWIDTH=$EXTERNAL_DEV_BANDWIDTH

if [ -z "$BNDWIDTH" ]; then
    BNDWIDTH="100mbit"
fi

## on clean deja les parametres en tout premier lieu
## On vide toutes les regles pouvant deja etre en place.
/sbin/tc qdisc del dev $EXTERNAL_IFACE root    2> /dev/null > /dev/null
/sbin/tc qdisc del dev $EXTERNAL_IFACE ingress 2> /dev/null > /dev/null


# install root CBQ # On met en place la policy root, les autres policies seront liées à celle-ci
## cbq avpkt : la taille moyenne des packets en bytes sont necessaire afin de calculer l'idle maximum
## bandwidth rate : pour determiner l'idle time cbq doit forcement connaitre la bande passante de l'interface physique.
/sbin/tc qdisc add dev $EXTERNAL_IFACE root handle 1: cbq avpkt 1000 bandwidth $BNDWIDTH

## allot bytes : utile pour calculer le temps de transmission des packets entre tables, valeur liée à avpkt
/sbin/tc class add dev $EXTERNAL_IFACE parent 1: classid 1:1 cbq rate ${UPLINK}kbit allot 1500 prio 5 bounded isolated


# voip (udp sip 5060 et asterisk 4569)
## Tout ce qui match cette règle possede une reservation CBQ de $EXTERNAL_UL_VOIP_KBPS | priorité numéro 1 !
if [ -n "$EXTERNAL_UL_VOIP_KBPS" ]; then
/sbin/tc class add dev $EXTERNAL_IFACE parent 1:1 classid 1:5 cbq rate ${EXTERNAL_UL_VOIP_KBPS}kbit allot 1600 prio 1 avpkt 1000 $EXTERNAL_UL_VOIP_SHARE

## pfifo : pure 'premier entré premier sortie' qdisc, évite la congestion de packet dans la classid 1:5
/sbin/tc qdisc add dev $EXTERNAL_IFACE parent 1:5 handle 5: pfifo limit 10

 # sip prioritaire 5 !
## filter: utile afin de savoir dans quel classfull qdisc, un packet va etre envoye.
## on peut filter les packets a partir de leur TOS : Type Of Service
/sbin/tc filter add dev $EXTERNAL_IFACE parent 1:0 protocol ip prio 5 u32 match ip sport 5060 0xffff match ip protocol 17 0xff flowid 1:5
/sbin/tc filter add dev $EXTERNAL_IFACE parent 1:0 protocol ip prio 5 u32 match ip dport 5060 0xffff match ip protocol 17 0xff flowid 1:5

# rtp (tos 0x10 et udp) (voir le tableau recap de TOS)
/sbin/tc filter add dev $EXTERNAL_IFACE parent 1:0 protocol ip prio 5 u32 match ip tos 0x10 0xff match ip protocol 17 0xff flowid 1:5

let UPLINK=UPLINK-EXTERNAL_UL_VOIP_KBPS
fi


# SMTP
if [ -n "$EXTERNAL_UL_SMTP_KBPS" ]; then
 /sbin/tc class add dev $EXTERNAL_IFACE parent 1:1 classid 1:44 \
 cbq rate ${EXTERNAL_UL_SMTP_KBPS}kbit allot 1600 prio 1 avpkt 1000 $EXTERNAL_UL_SMTP_SHARE
 /sbin/tc qdisc add dev $EXTERNAL_IFACE parent 1:44 handle 44: sfq perturb 10

 /sbin/tc filter add dev $EXTERNAL_IFACE parent 1:0 protocol ip prio 44 u32 \
 match ip dport 25 0xffff match ip protocol 6 0xFF match ip firstfrag flowid 1:44

 /sbin/tc filter add dev $EXTERNAL_IFACE parent 1:0 protocol ip prio 44 u32 \
 match ip dport 2525 0xffff match ip protocol 6 0xFF match ip firstfrag flowid 1:44
  
 /sbin/tc filter add dev $EXTERNAL_IFACE parent 1:0 protocol ip prio 44 u32 \
 match ip dport 587 0xffff match ip protocol 6 0xFF match ip firstfrag flowid 1:44

let UPLINK=UPLINK-EXTERNAL_UL_SMTP_KBPS
fi

# HTTP
if [ -n "$EXTERNAL_UL_HTTP_KBPS" ]; then
 /sbin/tc class add dev $EXTERNAL_IFACE parent 1:1 classid 1:46 \
 cbq rate ${EXTERNAL_UL_HTTP_KBPS}kbit allot 1600 prio 1 avpkt 1000 $EXTERNAL_UL_HTTP_SHARE
 /sbin/tc qdisc add dev $EXTERNAL_IFACE parent 1:46 handle 46: sfq perturb 10

 /sbin/tc filter add dev $EXTERNAL_IFACE parent 1:0 protocol ip prio 46 u32 \
 match ip dport 80 0xffff match ip protocol 6 0xFF match ip firstfrag flowid 1:46
 let UPLINK=UPLINK-EXTERNAL_UL_HTTP_KBPS
fi

# OTHERTCP
if [ -n "$EXTERNAL_UL_OTHERTCP_KBPS" ]; then
 /sbin/tc class add dev $EXTERNAL_IFACE parent 1:1 classid 1:48 \
 cbq rate ${EXTERNAL_UL_OTHERTCP_KBPS}kbit allot 1600 prio 1 avpkt 1000 $EXTERNAL_UL_OTHERTCP_SHARE
 /sbin/tc qdisc add dev $EXTERNAL_IFACE parent 1:48 handle 48: sfq perturb 10

 /sbin/tc filter add dev $EXTERNAL_IFACE parent 1:0 protocol ip prio 48 u32 \
 match ip dport $EXTERNAL_UL_OTHERTCP_PORT 0xffff match ip protocol 6 0xFF match ip firstfrag \
 flowid 1:48
 let UPLINK=UPLINK-EXTERNAL_UL_OTHERTCP_KBPS
fi

if [ $UPLINK -gt 0 ]; then

# classe interactive
/sbin/tc class add dev $EXTERNAL_IFACE parent 1:1 classid 1:10 cbq rate ${UPLINK}kbit allot 1600 prio 2 avpkt 1000
/sbin/tc qdisc add dev $EXTERNAL_IFACE parent 1:10 handle 10: sfq perturb 10

 # TOS Minimum Delay (ssh, NOT scp) in 1:10: donc on lui donne un priorité de 1000
 /sbin/tc filter add dev $EXTERNAL_IFACE parent 1:0 protocol ip prio 1000 u32 match ip tos 0x10 0xff  flowid 1:10

 # ICMP (ip protocol 1) dans la classe interactive 1:10 donc on lui donne un priorité de 1001 
 /sbin/tc filter add dev $EXTERNAL_IFACE parent 1:0 protocol ip prio 1001 u32 match ip protocol 1 0xff flowid 1:10

 # Pour accélérer le downaload lorsqu'un upload est en court il faut bien faire les ACK dans la classe interactive.

 /sbin/tc filter add dev $EXTERNAL_IFACE parent 1: protocol ip prio 1002 u32 match ip protocol 6 0xff match u8 0x05 0x0f at 0 match u16 0x0000 0xffc0 at 2 match u8 0x10 0xff at 33 flowid 1:10

# classe bulk

 /sbin/tc class add dev $EXTERNAL_IFACE parent 1:1 classid 1:20 cbq rate $[9*$UPLINK/10]kbit \
   allot 1600 prio 3 avpkt 1000
 /sbin/tc qdisc add dev $EXTERNAL_IFACE parent 1:20 handle 20: sfq perturb 10

 /sbin/tc filter add dev $EXTERNAL_IFACE parent 1: protocol ip prio 1003 u32 \
   match ip dst 0.0.0.0/0 flowid 1:20

else
 echo "Error: no bandwidth left for uplink on $EXTERNAL_IFACE: $UPLINK" 1>&2
 /sbin/tc qdisc del dev $EXTERNAL_IFACE root    2> /dev/null > /dev/null
fi

#
# Il faut ralentir les download afin d'éviter que l'ISP mette en queue ce qui engendre le lag 
# Les ISP ont tendance à avoir des queues énormes afin d'être sûrs que les downloads seront rapides.
#
# police ingress:

/sbin/tc qdisc add dev $EXTERNAL_IFACE handle ffff: ingress

# voip
# tout ce qui est voip ne doit pas être limité, donc on fait un match simple
# iax2

/sbin/tc filter add dev $EXTERNAL_IFACE parent ffff: protocol ip prio 5 u32 \
  match ip sport 4569 0xffff match ip protocol 17 0xff flowid :1
 /sbin/tc filter add dev $EXTERNAL_IFACE parent ffff: protocol ip prio 5 u32 \
  match ip dport 4569 0xffff match ip protocol 17 0xff flowid :1

# sip # priorite 5 !
/sbin/tc filter add dev $EXTERNAL_IFACE parent ffff: protocol ip prio 5 u32 \
  match ip sport 5060 0xffff match ip protocol 17 0xff flowid :1
/sbin/tc filter add dev $EXTERNAL_IFACE parent ffff: protocol ip prio 5 u32 \
  match ip dport 5060 0xffff match ip protocol 17 0xff flowid :1

/sbin/tc filter add dev $EXTERNAL_IFACE parent ffff: protocol ip prio 5 u32 \ 
match ip sport 5061 0xffff match ip protocol 17 0xff flowid :1

# rtp (tos 0x10 et udp) # priorte 5 !
/sbin/tc filter add dev $EXTERNAL_IFACE parent ffff: protocol ip prio 5 u32 \
  match ip tos 0x10 0xff match ip protocol 17 0xff flowid :1

# SMTP
if [ -n "$EXTERNAL_DL_SMTP_KBPS" ]; then
 /sbin/tc filter add dev $EXTERNAL_IFACE parent ffff: protocol ip prio 44 u32 \
 match ip sport 25 0xffff match ip protocol 6 0xFF match ip firstfrag \
 police rate ${EXTERNAL_DL_SMTP_KBPS}kbit burst 10k drop flowid :1
 let DOWNLINK=DOWNLINK-EXTERNAL_DL_SMTP_KBPS
fi

# HTTP
if [ -n "$EXTERNAL_DL_HTTP_KBPS" ]; then
 /sbin/tc filter add dev $EXTERNAL_IFACE parent ffff: protocol ip prio 46 u32 \
 match ip sport 80 0xffff match ip protocol 6 0xFF match ip firstfrag \
 police rate ${EXTERNAL_DL_HTTP_KBPS}kbit burst 10k drop flowid :1
 let DOWNLINK=DOWNLINK-EXTERNAL_DL_HTTP_KBPS
fi

# OTHERTCP qui ne matchent pas les règles précédentes
if [ -n "$EXTERNAL_DL_OTHERTCP_KBPS" ]; then
 /sbin/tc filter add dev $EXTERNAL_IFACE parent ffff: protocol ip prio 48 u32 \
 match ip sport $EXTERNAL_DL_OTHERTCP_PORT 0xffff match ip protocol 6 0xFF match ip firstfrag \
 police rate ${EXTERNAL_DL_OTHERTCP_KBPS}kbit burst 10k drop flowid :1
 let DOWNLINK=DOWNLINK-EXTERNAL_DL_OTHERTCP_KBPS
fi

##règles de burst
if [ $DOWNLINK -gt 0 ]; then
 /sbin/tc filter add dev $EXTERNAL_IFACE parent ffff: protocol ip prio 1000 u32 \
 match ip src 0.0.0.0/0 police rate ${DOWNLINK}kbit burst 10k drop flowid :1
else
 echo "Error: no bandwidth left for downlink on $EXTERNAL_IFACE: $DOWNLINK" 1>&2
 /sbin/tc qdisc del dev $EXTERNAL_IFACE ingress 2> /dev/null > /dev/null
fi

# initial if (are the 2 required variables defined?)
fi

N’hésitez pas à le tester, il déboite 🙂