Per specificare come indirizzare i pacchetti nelle classi corrispondenti è possibile utilizzare i filtri o classificatori. Ogni pacchetto che arriva è sottoposto ai filtri che decidono, in base alle indicazioni fornite, in quale classe/coda devono essere collocati.
Riprendendo il grafico precedente e aggiungendo i filtri si completa la realizzazione del tc basato su CBQ:
+------------------- QDISC CBQ -------------------+ |+--------------- CLASSE CBQ ROOT ---------------+| ______ || || / \ || +-CLASSE A 8Mbit prio 3 ---+ || | ROOT | || +---+ | +----------------+ | || \______/ Pacchetto -->==--+-|F1 |-= -> | SFQ |--> = ---+ || /10:1\ || | +---+ | +----------------+ | | || 8Mbit / \ 2Mbit || | +--------------------------+ | || ______ ______ || | +--> ==---> / \ / \ || | +-CLASSE B 2Mbit prio 2 ---+ | || |CLASSE A| |CLASSE B| || | +---+ | +--------------+ | | || \______/ \______/ || +-|F2 |-= -> | FIFO | ---> = ---+ || 10:100 10:200 || +---+ | +--------------+ | || || +--------------------------+ || |+-----------------------------------------------+| +-------------------------------------------------+
F1 e F2 sono i due filtri che in base alle impostazioni fornite provvedono a smistare i pacchetti nelle classi previste.
Per creare, rimuove, cambiare un filtro è necessario utilizzare, in modo simile alle discipline, il comando tc filter:
# tc filter help Usage: tc filter [ add | del | change | get ] dev STRING [ pref PRIO ] [ protocol PROTO ] [ estimator INTERVAL TIME_CONSTANT ] [ root | classid CLASSID ] [ handle FILTERID ] [ [ FILTER_TYPE ] [ help | OPTIONS ] ] tc filter show [ dev STRING ] [ root | parent CLASSID ] Where: FILTER_TYPE := { rsvp | u32 | fw | route | etc. } FILTERID := ... format depends on classifier, see there OPTIONS := ... try tc filter add <desired FILTER_KIND> help dev: si deve specificare l'interfaccia/dispositivo su cui si desidera controllare il traffico. (es. dev eth0). pref: priorità associata alla disciplina protocol: protocollo dei pacchetti che devono essere filtrati (es. protocol ip) estimator:estimatore handle: identificatore della classe verso cui devono essere inviati i pacchetti filtrati con successo FILTER_TYPE: filtro che si intende utilizzare (rsvp, route, fw, ...)
Si possono utilizzare diversi filtri, alcuni anche molto complessi e completi:
Utilizzando il programma ip, presente nel paccheto iproute2, è possibile associare ad ogni instradamento un "identificatore" che lo contraddistingua. Attraverso tc è possibile poi definire un filtro in cui si può specificare verso quale classe/coda deve essere inviato un pacchetto in base all'identificatore assegnato.
# tc filter add route help Usage: ... route [ from REALM | fromif TAG ] [ to REALM ] [ flowid CLASSID ] [ police POLICE_SPEC ] POLICE_SPEC := ... look at TBF CLASSID := X:Y from: provenienza dei pacchetti (REALM è il valore associato ad un instradamento con ip) to: destinazione dei pacchetti flowid: flusso, classe dove dirigere i pacchetti police: serve a specificare in determinati casi (numero eccessivo di pacchetti/bytes, raggiungimento limiti) l'azione (riclassifica, scarta, continua) da intraprendere. police rate BPS burst BYTES[/BYTES] [ mtu BYTES[/BYTES] ] [ peakrate BPS ] [ avrate BPS ] [ ACTION ] Where: ACTION := reclassify | drop | continue Esempio: -------- # ------------------------------------------------------------------------------------------ # Aggiungiamo un instradamento che diriga i pacchetti destinati all'indirizzo 192.168.0.0/24 # verso l'instradamento di default 192.168.0.1 interfaccia eth1, inoltre gli associamo il # valore identificativo 1 # ------------------------------------------------------------------------------------------ ip route add 192.168.0.0/24 via 192.168.0.1 dev eth1 realm 1 # ------------------------------------------------------------------------------------------ # I pacchetti destinati all'indirizzo 192.168.0.0/24 (to 1) sono diretti alla classe 1:10 # 'to 1' si riferisce all'instradamento indicato con ip route precedentemente (...realm 1) # ------------------------------------------------------------------------------------------ tc filter add dev eth1 parent 1:0 protocol ip prio 10 route to 1 classid 1:10 # ------------------------------------------------------------------------------------------ # I pacchetti provenienti dall'indirizzo 192.168.0.0/24 (from 1) sono diretti alla classe 1:10 # ------------------------------------------------------------------------------------------ tc filter add dev eth1 parent 1:0 protocol ip prio 10 route from 1 classid 1:10
Netfilter/iptables consentono di manipolare i pacchetti e in particolare di contrassegnarli con un numero utilizzando l'obiettivo -j MARK. Attraverso questo numero è possibile poi indicare a tc come trattare il pacchetto, verso quale classe indirizzarlo. E' probabilmente la soluzione più semplice ma non la più efficiente.
# tc filter add fw help Usage: ... fw [ classid CLASSID ] [ police POLICE_SPEC ] POLICE_SPEC := ... look at TBF CLASSID := X:Y classid: id (identificatore) della classe a cui inviare i pacchetti (es. 10:1) police: vedi sopra Esempio: -------- # ------------------------------------------------------------------------------------------ # impostiamo una variabile MAIL a cui associamo il valore 1 # ------------------------------------------------------------------------------------------ MAIL=1 # ------------------------------------------------------------------------------------------ # con iptables impostiamo una regola che modifichi i pacchetti destinati alla porta smtp # (--dport smtp) "marcandoli" con il valore MAIL (=1) (--setmark $MAIL). # ------------------------------------------------------------------------------------------ iptables -I PREROUTING -t mangle -p tcp -d ... --dport smtp -j MARK --setmark $MAIL # ------------------------------------------------------------------------------------------ # Aggiungiamo e associamo un filtro all'interfaccia eth1 (dev eth1) che per i pacchetti con # protocollo ip (protocol ip) e marcati con MAIL ossia 1 (handle $MAIL) provveda a inviarli # alla classe 1:1 (classid 1:1) # ------------------------------------------------------------------------------------------ tc filter add dev eth1 protocol ip parent 1:0 prio 1 handle $MAIL fw classid 1:1
Questa tecnica permette di filtrare i pacchetti in base al contenuto delle intestazioni (IP/TCP/...), quindi può permettere un'ottimo controllo. Permette inoltre di evitare l'utilizzo del filtraggio dei pacchetti (firewall) e in particolare l'utilizzo del marcamento dei pacchetti garantendo così maggior efficienza. Per ragioni di comodità è possibile utilizzare anche u16 o u8.
tc filter add u32 help Usage: ... u32 [ match SELECTOR ... ] [ link HTID ] [ classid CLASSID ] [ police POLICE_SPEC ] [ offset OFFSET_SPEC ] [ ht HTID ] [ hashkey HASHKEY_SPEC ] [ sample SAMPLE ] or u32 divisor DIVISOR Where: SELECTOR := SAMPLE SAMPLE ... SAMPLE := { ip | ip6 | udp | tcp | icmp | u{32|16|8} } SAMPLE_ARGS FILTERID := X:Y:Z match: lunghezza del pattern espressa in numero di bit (u32 = 32 bit, u16 = 16 bit, ...) link: classid: id (identificatore) della classe a cui inviare i pacchetti (es. 10:1) police: vedi sopra offset: scostamento in bytes (1 bytes = 8 bit), permette di "saltare" alcuni bytes ht: hash table hashkey: ... sample: ...
Il selettore U32 consente di specificare precisamente quali bits si desidera consultare nelle intestazioni. u32 specifica che il pattern deve essere di 32 bit, si può utilizzare se più comodo anche un patern da 16 bit (u16) o 8 bit (u8). Nel caso di pacchetti IP e TCP sono ad esempio consultabili i seguenti campi:
1 byte = 8 bit + 1mo byte + 2do byte + 3zo byte + 4to byte + 0 1 2 3 4 5 6 7|8 9 0 1 2 3 4 5|6 7 8 9 0 1 2 3|4 5 6 7 8 9 0 1 ----------------------------------------------------------------- |Version| IHL |Type of Service| Total Length | = 4 byte = 32 bit ----------------------------------------------------------------- | Identification |Flags| Fragment Offset | = 4 byte ----------------------------------------------------------------- | Time to Live | Protocol | Header Checksum | = ... ----------------------------------------------------------------- | Source Address | ----------------------------------------------------------------- | Destination Address | ----------------------------------------------------------------- | Options | Padding | ----------------------------------------------------------------- [INTESTAZIONE IP totale 20 byte] 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 ----------------------------------------------------------------- | Source Port | Destination Port | ----------------------------------------------------------------- | Sequence Number | ----------------------------------------------------------------- | Acknowledgment Number | ----------------------------------------------------------------- | Data | |U|A|P|R|S|F| | | Offset| Reserved |R|C|S|S|Y|I| Window | | | |G|K|H|T|N|N| | ----------------------------------------------------------------- | Checksum | Urgent Pointer | ----------------------------------------------------------------- [INTESTAZIONE TCP]
Per identificare i campi è necessario indicare il PATTERN (valore da confrontare) e la MASK (gruppo di bit interessati). Opzionalmente è possibile inoltre specificare uno scostamento (OFFSET) e l'intestazione (IP/TCP) che si desidera esaminare. I pacchetti TCP, UDP e ICMP sono incapsulati in pacchetti IP.
bit 0 1 2 3|4 5 6 7|8 9 0 1|2 3 4 5|6 7 8 9|0 1 2 3|4 5 6 7|8 9 0 1 ----------------------------------------------------------------- |Version| IHL |Type of Service| Total Length | = 4 byte = 32 bit ----------------------------------------------------------------- \__ ____/\___ __/ \ \______ \ | -------------v V protocol ip u32 match 0 0 00 0000 ^ ^ ^^ ^^^^ | | | '> 16 bit = Total lenght | | '> 8 bit = TOS | '> 4 bit = IHL '> 4 bit = version Per specificare TOS dovremo allora utilizzare come MASK 0 0 FF 0000. Per specificare IHL dovremo utilizzare come MASK 0 F 00 0000. Per specificare Total length .... 0 0 00 FFFF. Per spostarci sulla seconda linea è necessario usare l'OFFSET (scostamento) espresso in byte: bit 0 1 2 3|4 5 6 7|8 9 0 1|2 3 4 5|6 7 8 9|0 1 2 3|4 5 6 7|8 9 0 1 ----------------------------------------------------------------- | Identification |Flags| Fragment Offset | = 4 byte ----------------------------------------------------------------- \______________________________/ \ \__ v protocol ip u32 match 0000 0000 at 4 ^^^^ ^^^^ | '> scostamento OFFSET 4 byte '> 16 bit = Identification Per spostarci all'intestazione successiva è necessario utilizzare l'opzione nexthdr (eventualmente si può indicare anche un offset). protocol ip u32 match ........ at nexthdr+0
Questo metodo non è molto intuitivo per cui è stata aggiunta la possibilità di utilizzare al posto dei numeri delle stringhe:
---------------------------------------------------------------------- | Protocollo | Selettore | Parametri | Descrizione | | | | pattern mask | | ---------------------------------------------------------------------- | match ip | | | | | | src | prefix/32 | indirizzo IP sorgente | | | dst | prefix/32 | indirizzo IP destinazione| | | tos | tos u8 | TOS (Type Of Service) | | | dsfield | tos u8 | " (sinonimo) | | | precedence | tos u8 | " (sinonimo) | | | ihl | ihl u8 | campo IP Total Lenght | | | protocol | prot u8 | campo IP protocol | | | nofrag | | pacchetto non frammentato| | | firstfrag | | pacchetto primo frammento| | | df | | Don't Fragment impostato | | | mf | | More Fragments impostato | | | sport | port u16 | Porta sorgente (tcp/udp)| | | dport | port u16 | Porta destinazione (") | | | icmp_type | type u8 | tipo pacchetto ICMP | | | icmp_code | code u8 | codice pacchetto ICMP | |match ip6 | | | | | | src | prefix/128 | indirizzo IPv6 sorgente | | | dst | prefix/128 | indirizzo IPv6 dest. | | | flowlabel | flow u32 | IPv6 flow label | |match udp,tcp| | | | | | src | src u16 | porta sorgente | | | dst | dst u16 | porta destinazione | |match icmp | | | | | | type | type u8 | tipo pacchetto ICMP | | | code | code u8 | codice pacchetto ICMP | |_____________|_____________|______________|__________________________| u8 = 0xff u16 = 0xffff u32 = 0xffffffff USO --- protocollo selettore PATTERN (www) MASK (u16) / / / / tc filter add ... u32 match tcp dst 80 0xffff tc filter add ... u32 match ip dst 192.168.0.1/24 match ip protocol 6 0xff match ip tos 0x10 0xff Esempi: -------- # ------------------------------------------------------------------------------------------ # Crea un filtro che identifichi i pacchetti ip con bit tos pari a 0x10 e li invii alla # classe 1:4. 0xff è la maschera ossia i bit da controllare. # ------------------------------------------------------------------------------------------ tc filter add dev eth0 parent 1:10 prio 10 u32 match ip tos 0x10 0xff flowid 1:4 # ------------------------------------------------------------------------------------------ # Crea un filtro che identifichi i pacchetti tcp (campo Protocol presente nell'intestazione # ip impostato a tcp ossia 0x6) con porta destinazione (presente nell'intestazione tcp) # pari a 80 (www) e li invii alla classe 1:3. 0xffff e 0xff sono le maschera. # ------------------------------------------------------------------------------------------ tc filter add dev eth0 parent 1:10 prio 10 u32 match tcp dport 80 0xffff match ip protocol 0x6 0xff flowid 1:3
rsvp presenta i seguenti parametri:
tc filter add rsvp help Usage: ... rsvp ipproto PROTOCOL session DST[/PORT | GPI ] [ sender SRC[/PORT | GPI ] [ classid CLASSID ] [ police POLICE_SPEC ] [ tunnelid ID ] [ tunnel ID skip NUMBER ] Where: GPI := { flowlabel NUMBER | spi/ah SPI | spi/esp SPI | u{8|16|32} NUMBER mask MASK at OFFSET} POLICE_SPEC := ... look at TBF FILTERID := X:Y ipproto: protocollo session: indirizzo destinazione ed eventuale porta sender: indirizzo sorgente e porta classid: identificatore della classe police: ... es.: tc filter add dev eth0 parent 1:10 protocol ip rsvp ipproto udp session 10.0.0.1 flowid 1:200