Qualche giorno fa ho dovuto far fronte ad un problema di sovraccarico di rete di alcuni server per un provider americano che eroga servizi di anonimato. Nello specifico, proxy e VPN. Ho dovuto quindi stendere un piccolo script per poter fare un po’ di traffic shaping. Ecco come l’ho creato.
Ambiente e servizi
Prima di tutto ho dato un’occhiata al traffico e al modo in cui la macchina eroga i servizi.
Il carico di rete alto proveniva essenzialmente dai proxy, che vengono usati per fare spidering dei siti internet. Si parla quindi di una grande mole di connessioni simultanee da parte dello stesso IP.
I proxy venivano anche usati per effettuare il download di files o lo streaming di flussi video. Ho provato poi a configurare un account nella mia macchina per controllare il numero medio di connessioni simultanee per un utilizzo standard del servizio ed ho visto che tocca punte di 40 connessioni (posta, browsing, ed altre piccole cose), mentre alcuni IP arrivavano anche a 500-600 connessioni.
Il proxy è in ascolto in porta 80.
Per una maggiore usabilità ho chiaramente dovuto escludere dei servizi dal sistema di shaping, come SSH, lasciandoli “liberi di correre”.
Ho deciso quindi di utilizzare un semplice traffic shaping avvalendomi di 2 tools molto potenti: TC e Iptables, che mi hanno consentito di creare una catena dinamica delle connessioni e dei rispettivi limiti di banda.
Ecco come funziona:
- La banda viene limitata secondo le possibilità fisiche: se il provider ti offre un limite 10mbit di banda, pena un sovrapprezzo per megabyte, dovremo accuratamente settare la quantità di banda consentita
- Le prime 50 connessioni simultanee vengono accodate a priorità 1 a massima velocità
- Dalla connessione 51, le connessioni vengono accodate in una catena a banda limitata
- I files vengono scaricati a piena velocità da 0 a 9.99Mbyte, da 10Mbyte a 49.99 Mbyte vengono accodati in una coda limitata, e da 50Mbyte in poi, in una coda ancor più limitata.
- Possibilità di utilizzare solo una parte della banda totale, utilizzando i parametri “DESIRED” e “DEFAULT”
Vediamo un po’ come implementare questa soluzione:
Prima di tutto creo una sezione per la configurazione delle variabili che utilizzerà lo script:
# Configuration part: # $IPT binary IPT=/sbin/iptables # Tc binary TC=/sbin/tc # Physical interface - no virtual interfaces allowed ($ETH:0, $ETH:1 etc) ETH=eth0 # SSH server port - we accept all connections in order to bypass the shaper SSH_PORT=131 # Total bw rate TOTAL_BW=10mbit # Bandwidth peak - Please set it a little lower than your real preference PEAK_BW=10mbit # Desired Bandwidth Rate - Useful if you want to use less bw of your real capacity DESIRED_BW=9mbit # Desired peak - Same as above DESIRED_PEAK=10mbit # Jail rate - Set it quite lower if you want real results JAIL_BW=512mbit # Jail peak - Same as above JAIL_PEAK=1mbit # Default Bandwidth # Set: 1 for Total values # 2 for Desired values DEFAULT_BW=2 # Connection Limit - Note that we talk about symultaneous connections. A good value is 10 CONNLIMIT=30 # Set first size limit FIRST_SIZE=10485760 # Set the total available bandwidth for the first size limit FIRST_SIZE_BW=1mbit # Set the total available peak for the first size limit FIRST_SIZE_PEAK=1mbit # Set second size limit SECOND_SIZE=52428800 # Set the total available bandwidth for the second size limit SECOND_SIZE_BW=512kbit # Set the total available peak for the second size limit FIRST_SIZE_BW_PEAK=512kbit # End of configuration file
Dopodiché creo lo script vero e proprio:
Prima di tutto faccio un flush della tavola di mangle (che serve per il mark dei pacchetti) e della catena di OUTPUT – che è l’unica che utilizzerò -:
# Flush all queues $TC qdisc del dev $ETH root # Flush mangle chains $IPT -t mangle -F $IPT -t mangle -Z $IPT -t mangle -X $IPT -F OUTPUT
A questo punto posso creare la qdisc, con le relative classi:
# Creating New TC queue $TC qdisc add dev $ETH root handle 1: htb default $DEFAULT_BW $TC class add dev $ETH parent 1: classid 1:1 htb rate $TOTAL_BW ceil $PEAK_BW $TC class add dev $ETH parent 1:1 classid 1:2 htb rate $DESIRED_BW ceil $DESIRED_PEAK $TC class add dev $ETH parent 1:1 classid 1:3 htb rate $JAIL_BW ceil $JAIL_PEAK $TC class add dev $ETH parent 1:1 classid 1:4 htb rate $FIRST_SIZE_BW ceil $FIRST_SIZE_PEAK $TC class add dev $ETH parent 1:1 classid 1:5 htb rate $SECOND_SIZE_BW ceil $FIRST_SIZE_BW_PEAK Poi aggancio le classi al device principale:
# Adding the queue lists to the main device $TC qdisc add dev $ETH parent 1:2 sfq $TC qdisc add dev $ETH parent 1:3 sfq $TC qdisc add dev $ETH parent 1:4 sfq $TC qdisc add dev $ETH parent 1:5 sfq
Dico a TC di effettuare il traffic shaping secondo il mark dei pacchetti di Iptables:
# Adding filter by packet mark $TC filter add dev $ETH parent 1:0 protocol ip prio 1 handle 7 fw flowid 1:3 $TC filter add dev $ETH parent 1:0 protocol ip prio 1 handle 20 fw flowid 1:4 $TC filter add dev $ETH parent 1:0 protocol ip prio 1 handle 21 fw flowid 1:5
Ed infine creo le regole di MARK su Iptables e il canale preferenziale per SSH:
# Setting packet connmarks: $IPT -A PREROUTING -t mangle -j CONNMARK --restore-mark $IPT -A POSTROUTING -t mangle -m mark ! --mark 0 -j ACCEPT $IPT -A POSTROUTING -t mangle -p tcp -m connlimit --connlimit-above $CONNLIMIT -j MARK --set-mark 7 $IPT -A POSTROUTING -t mangle -j CONNMARK --save-mark # Setting file size mark $IPT -A OUTPUT -p tcp --sport $SSH_PORT -j ACCEPT $IPT -A OUTPUT -p tcp -o $ETH -m connbytes --connbytes $SECOND_SIZE: --connbytes-dir both --connbytes-mode bytes -j MARK --set-mark 21 $IPT -A OUTPUT -p tcp -o $ETH -m connbytes --connbytes $FIRST_SIZE:$SECOND_SIZE --connbytes-dir both --connbytes-mode bytes -j MARK --set-mark 20 $IPT -A OUTPUT -p tcp -m connlimit --connlimit-above $CONNLIMIT -j MARK --set-mark 7
Conclusione
Non sono sicuro al 100% che questo script sia stato steso in maniera perfetta, certo è che funziona abbastanza bene. I carichi sui suddetti server sono spariti e la navigazione con i proxy è fluida e veloce, nonostante ora venga utilizzato solamente circa un terzo della banda.