Otthoni és irodai hálózatok a gyakorlatban - Labor: QoS

A VIK Wikiből

Ez az oldal a korábbi SCH wikiről lett áthozva.

Ha úgy érzed, hogy bármilyen formázási vagy tartalmi probléma van vele, akkor, kérlek, javíts rajta egy rövid szerkesztéssel!

Ha nem tudod, hogyan indulj el, olvasd el a migrálási útmutatót.



%TOC{depth="3"}%

Laborvezető: Dávid Róbert (david@tmit.bme.hu, dr420@hszk.bme.hu)

Bevezetés

Aki huzamosabb ideje internetezik, és sokszor tölt fel/le például FTP-vel vagy BitTorrentel, az biztos észrevette, hogy ekkor a weboldalak lassabban jönnek be, a VoIP beszélgetés elkezd darabossá válni, vagy akár a hálózatos játékban megnövekedik a késleletetés. Az is nagyon gyakori jelenség, hogy az egyik irányba erős forgalom tönkreteszi a másik irányú forgalmat. Miért történik ez, mit lehet ellene tenni? A mérés során erre keresünk választ.

Elméleti áttekintés

Az itt leírtak összefoglalása a www.lartc.org-ról letölthető Linux Advanced Routing & Traffic Control 9. fejezetének. Ha valami nem egyértelmű, nézzetek ott utána (és szóljatok, hogy javíthassuk a mérést)!

Az internet-szolgáltatók (ISP-k) pontosan tudják, hogy az előfizetők legnagyobb része szinte csak az átviteli sebesség alapján méri a szolgáltatás jóságát, minden más paraméter — például a késleltetés — nagyon ritkán fordul elő azok közt, amire a szolgáltató büszke, és ezt hirdeti is. Egy link sávszélességét úgy lehet legjobban kihasználni, ha minél nagyobb a link végén ülő eszközben a puffer: ha sosem fogy ki, mindig maximális adatsebességgel lehet továbbítani a csomagokat. Ezért általában az ADSL és kábelmodemekben igen nagy, több MiB méretű puffer található.

A modemben a puffer tisztán FIFO működésű. Ha a puffer tele van, azaz például az FTP feltölti ezt csomagokkal, akkor az interaktív forgalom épp érkezett csomagjának ki kell várnia, míg a teljes puffernyi adat átmegy a linken — ez adott esetben másodperc nagyságrendű késleltetés is lehet! Általános esetben a modem egy 100 Mbites Etherneten keresztül kapcsolódik a számítógéphez, belső hálózathoz, így korlátozás nélkül a puffer könnyen feltöltődik. Ez a magyarázat az első kérdésre.

A másik probléma is ugyanez okokra, valamint a TCP működésére vezethető vissza: a letöltés TCP kapcsolaton keresztül megy, és a TCP ACK csomag nem jut át elég gyorsan a küldőhöz, ezért az visszaveszi a küldési sebességet.

Mi tehát a megoldás? Meg kell akadályozni, hogy a modem puffere telítődjön, sőt, egyáltalán csak épp az a csomag legyen benne, amit neki következőleg el kell küldenie. Ehhez a puffert „helyezzük át” egy olyan eszközbe, ahol erre van ráhatásunk, és az interaktív forgalom csomagját soron kívül továbbítani a router felé, a többi forgalomból pedig csak annyit szabad átküldeni, amennyit a modem azonnal továbbítani is tud. A Linux minden ehhez szükséges eszközt megad, a mérés során tapasztaltak minden (legalább 2.4-es kernellel rendelkező) Linuxra érvényesek. De ezek alapvetően routing feladatok, ezért a mérést Linuxos (OpenWRT-s) routerrel fogjuk elvégezni.

Sorállási diszcliplínák

A Linux sorállási diszciplínának (Queing Discipline, qdisc) nevezi a csomagok sorba állításának módját. Több algoritmust is megvalósítottak már a programozók, ezek közül párat fogunk áttekinteni. Két alapvető típust tudunk megkülönböztetni: az osztálymentes és az osztályozó qdisc. Utóbbi a továbbítandó csomagokat osztályokba rendezi, és az osztályok prioritása alapján dönti el, melyik osztályból továbbit csomagokat.

A különböző diszciplínáknak különböző paraméterei vannak, a legfontosabbak a lartc.org-on a megfelelő diszciplína fejezetében elolvashatjuk, illetve a mérés során a mérés vezetője ismerteti.

Osztálymentes qdisc-ek

Három osztálymentes qdisc érdemes említésre: a pfifo/bfifo, a TBF és a SFQ

bfifo, pfifo, pfifo_fast

Egyszerű fifo. Ami csomag érkezett bele, amint lehetséges, továbbítja a hálózati interfészen. Semmilyen prioritást vagy korlátozást nem alkalmaz. Egyedüli haszna, hogy (mint minden más qdisc) statisztikát készít — a pfifo a csomagok számáról, a bfifo pedig a byte-ok számáról.

A pfifo_fast ennél más okosabb, megnézi a csomagok Type of Service (ToS) mezőjét, és ez alapján rendezi három sávba a csomagokat. Interaktív, normál és háttér osztályokba. A probléma, hogy ez nem konfigurálható.

Token Bucket Filter (TBF)

A tokenes vödör algoritmus megvalósítása: a pufferből akkor továbbítja a csomagot, ha van szabad token — a tokenek egy előre meghatározott sebességgel érkeznek, így ez az algoritmus hasznos a linkre küldött adatok sebességének korlátozására, ez által nem árasztjuk el a modemet a 100 Mbites Etherneten keresztül.

Stochastic Fairness Queing (SFQ)

Ennek a qdiscnek a célja, hogy ha több adatfolyam fut egy linken keresztül, akkor mindegyik folyam azonos sávszélességet kapjon. Azért sztochasztikus, mert nem pontos algoritmus, csak „valószínűleg” azonos sávszélt fognak a folyamok kapni. Ezt úgy éri el, hogy a csomagokat folyamokhoz rendeli (ip címek, portok alapján), majd ez alapján beteszi egy hash táblába. Amikor csomagot küld, mindig a hashtábla másik cellájából küld egy csomagot, így mindig más folyam kerül továbbításra. A hash függvényt néha újrakonfigurálja, hogy az azonos cellába került folyamok csak rövid ideig versenyezzenek az adott cellára jutó sávszélességért (újrakonfigurálás után valószínűleg másik cellába fognak kerülni)

Osztályozó qdisc-ek

Az osztályozó qdisc-ek fastruktúra alakban alakítják ki az osztályokat. A hálózati interfészhez kapcsolódó „gyökér” osztály az „1:0” nevű osztály (a : után a 0 elhagyható — *bdquo;1:”). Az alosztályok 1:xy alakúak. Az alosztályokhoz is további alosztályok kapcsolódhatnak.

Minden osztályban szűrők helyezkednek el, amelyek eldöntik, melyik alosztályba (nem feltétlen csak közvetlen gyerekhez, annak gyerekeihez is lehet) továbbítsák a kapott csomagot, így e szűrőkkel lehet beállítani, hogy az interaktív forgalom jobb prioritást kapjon — ha olyan osztályba küldjük, aminek nagyobb prioritást adunk.

Az osztályozott qdisc levele alá más diszciplínát is lehet kapcsolni, ezek azonosítója ab:xy alakúak (pl. annak a gyökere 10:, ha osztályozó qdisc, akkor egy levele például 10:1).

Csomagtovábbításkor a kernel a gyökér osztálytól fogja elkérni a csomagot, de az továbbítja a kérést egy alosztályhoz — amíg csomagot nem talál.

A PRIO qdisc

A legegyszerűbb osztályozó qdisc. Egy osztálya mindig a kisebb sorszámú, azaz itt nagyobb prioritású alosztályából kéri el a csomagot továbbításkor, bármennyi kisebb prioritású csomag is legyen. Ezzel kemény prioritást lehet elérni, semmilyen esetben nem fog kisebb prioritású csomag nagyobb előtt továbbításra kerülni. A szűrőket bármelyik — gyerekkel rendelkező osztályba elhelyezhetjük.

Hierarchical Token Bucket (HTB)

Ezen algoritmusnál az alosztályoknak garantált sávszélességet tudunk adni. Például, ha három számítógép van a hálózatban, akkor mindegyiknek garantálhatjuk a sávszélesség harmadát — de ha valamelyik nem forgalmaz, akkor a fennmaradt harmadot a másik két gép között egyenletesen feloszthatjuk. Vagy, ha az egyik gépet preferáljuk (mert például, ő többet fizet), akkor nagyobb garantált sávszélt kaphat, és nagyobb arányban részesülhet a maradék sávszélességből. Az algoritmus megvalósítása miatt csak a gyökérbe helyezhetjük el a szűrőinket.

CBQ

Ez az algoritmus a qdisc-ek Omegája. Rengeteg dolgot lehet benne állítani, de ennek megfelelően rengeteg paramétere van. A HTB ennek egyszerűsítéseként is felfogható, ezért inkább azt használjuk a mérés alatt. Akit érdekel, nézzen utána a www.lartc.org-on.

Csomagosztályozó szűrők

Az osztályozást csomagonként kell elvégezni, erre a Linuxban szűrők hivatottak. Amint egy csomag megérkezik egy osztályba, az osztályban található szűrők kitalálják, melyik alosztályba kell továbbítani a csomagot. Mi alapján lehet szűrni? Mindenre, ami az IP csomagban megtalálható. Vegyük a legfontosabbakat sorra:

  • Protokoll: Milyen protokoll található az IP csomagban: TCP, UDP, ICMP, IPSEC, stb. A protokollokat az IP csomagbeli azonosítószámuk alapján határozhatjuk meg. Az ICMP például az 1-es számú.
  • IP címek, portok: Forrás és célcímek, portok alapján is végezhető a szűrés, ez megadható tartományonként is.
  • ToS mező: Az IP csomagokban található egy négybites mező, minden bitje egy kérést fogalmaz meg a csomag továbbítása felé: az első bit a „minimize delay” (minimális késleltetés), második a „maximize throughput” (maximális sávszélesség), a harmadik a „maximize reliability” (maximális megbízhatóság), a negyedik pedig a „minimize monetary cost” (minimális pénzügyi költség). Egyszerre egy vagy több bitre is lehet szűrni, egy bitmaszkon keresztül.
  • U8/U16/U32 szűrők: a csomag bármilyen számjegyére (8 16 ill. 32 bites számjegyeket tekintve) illesztett szűrő, mi adjuk meg, hányadik számjegy(ek) érdekel(nek) minket. Alapjában véve az előző szűrők mind U32 szűrők, mivel a protokoll, IP cím és ToS mező az IP csomag pontosam meghatározott helyén helyezkedik el.
  • Forward mark alapján: azon csomagoknál alkalmazható, amelyek az IPtables-ön keresztül érkeznek az interfészre. Ottani szabályok megjelölhetik (mark) a csomagokat, és e mark alapján történik a döntés.
  • 7. rétegbeli szűrő: forward mark használatával van ezek használatára (mert jelenleg csak az IPtables-hez van csak implementálva). Ezek a szűrők alkalmazás szinten próbálják azonosítani a protokollt. Hátrányuk, hogy néha nem megbízhatóak, kihagynak csomagokat, vagy olyanokat jelölnek meg, amiket nem kellene. De sok protokoll jól beazonosítható (pl. a HTTP protokoll csomagjai mindig HTTP karakterekkel kezdődnek), így például más porton működtetett szerverek esetén is találatot eredményeznek.

Ingress qdisc

Az eddig leírtak az interfészen kifelé irányult forgalomra vonatkoznak. Van lehetőség befelé jövő qdisc-ek használatára, de ezek korlátozottabbak (de nincs is értelme befelé sebességet korlátozni — egyből a továbbításra vagy a helyi folyamathoz kerül a csomag).

Ha vlan-okat alkalmazunk, mint ahogy az alkalmazott routerben is, akkor is a valódi interfészen fog megjelenni a csomag. Tehát például a router WLAN portja a kernel számára az eth0 proton a vlan0 interfész, de NEM a vlan0-n fog megjelenni az Internet felől érkező csomag, hanem az eth0-n.

Intermediate Queing Device (IMQ)

A qdisc-eket egy-egy hálózati interfészhez tudjuk hozzárendelni — ez lehet virtuális interfész is, pl. vlan1 (vlan-hoz), br0 (több interfész közti bridge esetén). Ekkor figyelni kell, hogy mindkét diszciplínán végig fog jutni a csomag, először a virtuálison, utána a fizikai interfészhez tartozón. A felfogás hátránya, hogy nem lehet globális szabályokat létrehozni.

Ezért született meg az IMQ. Ez egy virtuális hálózati interfész, így minden megtehető, amit egy interfész qdisc-jeivel tudunk. IPtables-el megjelölt csomagok kerülhetnek be ide, például, ha akarjuk, minden csomagot is egyenesen ide irányíthatunk, ez által globális szabályozást végezhetünk. Több IMQ is létezhet egyszerre, például, külön kezelhetjük így az új kapcsolatok létrehozását, a http forgalmat, egy interfészről jött forgalmakat is.

A tc parancs

Linux alatt a fentieket a tc paranccsal állíthatjuk be. Itt csak a mérés során érdekes részeket mutatjuk be, akit bővebben érdekel, www.lartc.org :)

Qdisc-ek beállítása

Gyökér qdisc hozzáadása

tc qdisc add dev [eszköznév] root handle 1: [qdisc típusa] [qdisc paraméterek]

  • Az eszköznév pl. =eth0=.
  • qdisc típusa: az algoritmus HBR-je (hárombetűs rövidítés), kivéve a =bfifo=, =pfifo=, =pfifo_fast= esetében.
  • qdisc paraméterek: az adott algoritmus paraméterei paraméternév1-szóköz-érték1 -szóköz-paraméter2-szóköz-érték2 formában. Pl. =rate 512kbit burst 6k prio 1=.

Alosztály egy qdisc alá

tc qdisc add dev [eszköznév] parent [szülő azonosítója] classid [azonosító] [qdisc típusa] [qdisc paraméterek]

  • Azonosító, szülő azonosítója: a megfelelő osztály azonosítója például =1:10=
  • A többi paraméter hasonlóan az előzőhöz.

Qdisc törlése

tc qdisc del dev [eszköznév] [honnan]

  • Honnan: =root=, ha gyökér, vagy =parent [azonosító]=, ha alosztály, amit törölni szeretnénk.

Szűrők hozzáadása

Mindegyik a következőképpen kezdődik:

tc filter add dev [eszköznév] parent [osztály] protocol ip prio [prioritás] [szűrő típusa] [szűrő paraméterek] flowid [alosztály]

  • Eszköznév: mint a qdisc-ek esetében. Azért kell mindenhol megadni, mert eszközönként azonosítja a Linux, hova kell elhelyezni a szűrőt.
  • Szűrő prioritás: ha több szűrőt rendelünk egy helyre (igazából különben nincs értelme..), akkor ezek közül milyen sorrendben hajtsa őket vére. Azonos prioritásúak közül nem meghatározott, melyik fut le előbb, és mivel az első pozitív találat kiviszi az osztályból a csomagot, csak egy fog lefutni.
  • Szűrő típusa: Kétféle szűrővel foglalkozunk most: forward mark (
    fw
    ) alapú és =u32= szűrőkkel (a többi elméletben említett mind =u32= szűrő). Van több is, pl. =rsvp= (ReSource reserVation Protokoll) szűrő, =route= szűrő, stb.
  • Alosztály: azon alosztály azonosítója (pl. 1:20), ahova továbbítjuk a csomagot találat esetén.

IP cím alapú szűrő hozzáadása egy osztályhoz

tc filter add dev [eszköznév] parent [osztály] protocol ip prio [prioritás] U32 match ip [mit] [alháló cím]/[netmask] flowid [melyik alosztály]

  • Mit: a csomag melyik részére illesszen. Lehet src vagy dst, értelemszerűen forrás és cél IP-re illesztést jelent.
  • Alháló cím: IP4 esetén xx.xx.xx.xx alakú: a hálózati cím, amire illeszteni akarunk.
  • Netmask: az illesztett hálózatot az IP címtartomány hány bitje 1-es: pl. a Schönherz

kollégium címtartománya 152.66.204.0-152.66.215.255, igy a cím: 152.66.204.0/21

Port alapú szűrő hozzáadása egy osztályhoz

tc filter add dev [eszköznév] parent [osztály] protocol ip prio [prioritás] U32 match ip [mit] [portcím] [bitmask] flowid [melyik alosztály]

  • Mit: a csomag melyik részére illesszen. Lehet =sport= vagy =dport=, értelemszerűen forrás és cél portra illesztést jelent.
  • Portcím: a port száma.
  • Bitmask: ez határozza meg, a port melyik bitjeinek kell egyeznie a csomagban a megadottal. Ha pontos port számot adunk meg (szinte mindig), akkor ez Oxffff (32 bitnyi egyes)

Protokoll alapú szűrő hozzáadása egy osztályhoz

tc filter add dev [eszköznév] parent [osztály] protocol ip prio [prioritás] U32 match ip protocol [protokollazonosító] [bitmask] flowid [melyik alosztály]

  • Protokollazonosító: a protokoll IP csomagban megadott száma. Pl. az ICMP az 1-es számú protokoll.
  • Bitmask: ez határozza meg, a port melyik bitjeinek kell egyeznie a csomagban a megadottal. Értelemszerűen mind egyezzen, így most 0xff (ez a mező 16 bites, amire illesztettünk — 16 bit 1-es most)

ToS mező alapú szűrő hozzáadása egy osztályhoz

tc filter add dev [eszköznév] parent [osztály] protocol ip prio [prioritás] U32 match ip ToS [mivel] [bitmaszk] flowid [melyik alosztály]

  • A fenti példákhoz hasonlóan működik, bitmaszkon keresztüli illesztésről van szó. A különbség, hogy itt ez előzőekkel ellentétben tényleg csak egy-két bitre akarunk szűrni: így a bitmaszk nem feltétlen 0xff. A mező 8 bites, az első 3 jelentése precedencia (csomag prioritás), a következő 4 az elméletben felsoroltak a megfelelő sorrendben, az utolsóedi egy ellenőrző bit. Így például, a késleltetésre a 0x10

bitmaszkkal tudunk illeszteni. (mivel ez hátulról az 5. bit)

Forward mark alapú szűrő hozzáadása egy osztályhoz

tc filter add dev [eszköznév] parent [osztály] protocol ip prio [prioritás] handle [mark] fw flowid [melyik alosztály]

  • Mark: az IPtables-ben beállított jelölése a csomagnak, egy egész szám.

L7 szűrő használatával így tudunk ilyen jelölést adni

iptables -t mangle -A POSTROUTING -m layer7 --l7proto [protokoll] H MARK --set-mark [mark]

  • Protokoll: az l7 szűrő által ismert protokollokat adhatjuk meg itt. Ez a megfelelő könyvtárban szereplő protokoll leíró fájlokban testesül meg &#mdash; Saját protokoll definíciós fájlokat is hozzáadhatunk.(Lásd: [[1]])
  • Mark: az előző paranccsal megegyező jelölés. Több IPtables szűrő is adhat ugyanolyan jelölést, a csomagok azonosan lesznek kezelve a qdisc-en belül

Feladatok

A feladatok célja, hogy megismerjétek, milyen hatása van az egyes beállításoknak. Interaktív forgalom http legyen a mérés során (böngésszetek webet :)), nagy sávszélesség igényű adatforgalom céljából pedig ftp le-/feltöltés lesz. A feladatok után írjátok le a „böngészési élményt”.

  1. A mérési környezetet elő kell készíteni. A routerben állítsátok át a LAN oldal címét 192.168.2.0/24-re!
  2. Mivel a laborban nincs ADSL modem, mint otthon lenne, szimuláljuk a routerrel! Gondoljuk végig, milyen karakterisztikája van egy nagy pufferrel rendelkező modemnek, és korlátozzuk a sávszélességünket ennek megfelelően 128 kbit/s sebességre mindkét irányban. Használjatok a HTB-t.
  3. Az előző feladatban beállított HTB alá hozzatok létre egy másik osztályozó qdisc-et. Hozzatok létre osztályokat az interaktív és a háttér forgalomnak.
  4. Állítsatok be port alapú szűrőket. Menjetek el nem 80-as porton működő weboldalra is!
  5. Keressétek meg az FTP és HTTP protokoll számára ajánlott ToS értéket. Állítsatok be ez alapján ToS alapú szűrőket. (Ne felejtsétek el az előzőeket törölni)
  6. Állítsatok be L7 protokoll alapú szűrőket a HTTP és FTP forgalomra.

-- Peti - 2006.12.04.

Kiegészítés a mérési utasításhoz

A méréshez szükséges kernel modulok

  • =insmod ipt_owner=
  • =insmod ipt_tos=
  • =insmod sch_htb=
  • =insmod sch_sfq=
  • =insmod sch_ingress=
  • =insmod ipt_layer7=
  • =insmod cls_u32=
  • =insmod cls_fw=

MAC cím klónozás

  • =nvram set wan_hwaddr="aa:bb:cc:dd:ee:ff"=

IP beállítása 192.168.2.0/24-re:

  • =nvram set lan_ifname=br0=
  • =nvram set lan_ifnames="vlan0 eth1"=
  • =nvram set lan_proto=static=
  • =nvram set lan_ipaddr=192.168.2.1=
  • =nvram set lan_netmask=255.255.255.0=

WAN DHCP:

  • =nvram set wan_ifname=vlan1=
  • =nvram set wan_proto=dhcp=

LAN DHCP:

  • =nvram set dhcp_start=100=
  • =nvram set dhcp_num=50=

Példa TC parancsok

Gyökér HTB létrehozása, minden szűrők által nem kiválasztott csomagot továbbítson az 1:20-ba:

  • =tc qdisc add dev vlan1 root handle 1: htb default 20=

1:1 osztály létrehozása, szabályozzuk 128 kbitre, de 6k méretű löketeket engedélyezünk.

  • =tc class add dev vlan1 parent 1: classid 1:1 htb rate 128kbit burst 6k=

1:10-es osztály létrehozása 1:1 alá (ezért ez is korlátozott), 1-es prioritással

  • =tc class add dev vlan1 parent 1:1 classid 1:10 htb prio 1=

1:20 osztály a többi csomagnak, kisebb prioritással:

  • =tc class add dev vlan1 parent 1:1 classid 1:20 htb prio 5=

SFQ hozzáadása 1:10 alá, hogy az összes kapcsolat azonos sávszélt kapjon:

  • =tc qdisc add dev vlan1 parent 1:10 handle 10: sfq perturb 16=

TOS Minimum Delay jelzőbites csomagok 1:10-be kerülnek:

  • =tc filter add dev vlan1 parent 1:0 protocol ip prio 10 u32 match ip tos 0x10 0xff flowid 1:10=

80-as célportú csomagok 1:10-be:

  • =tc filter add dev vlan1 parent 1:0 protocol ip prio 15 u32 match ip dport 80 0xffff flowid 1:10=

ICMP (ip protocol 1) is 1:10-be kerül (verjünk át mindenkit, aki pingel..)

  • =tc filter add dev vlan1 parent 1:0 protocol ip prio 10 u32 match ip protocol 1 0xff flowid 1:10=

L7 szűrő alkalmazása WoW protokollra, hogy jó legyen a játékban a késleltetés:

  1. IPTables-ben megjelöljük a csomagot:
    • =iptables -t mangle -A POSTROUTING -m layer7 --l7proto worldofwarcraft -j MARK --set-mark 10=
  1. TC-vel a jelölés alapján 1:10-be kerül a csomag:
    • =tc filter add dev vlan1 parent 1:0 protocol ip prio 1 handle 10 fw flowid 1:10=

TCP ACK csomagok is magas prioritásúak, így a nagy feltöltés nem öli meg a letöltéseket:

  • =tc filter add dev vlan1 parent 1: protocol ip prio 10 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=

Letöltés „korlátozás”: TCP visszaveszi a sebességet csomagvesztéskor, ezt használjuk ki: hozzunk létre egy ingress sort, ami egy határnál (állítsuk be a valós sebesség 99%-ára..) gyorsabban jött le, dobjuk el.

  • =tc qdisc add dev vlan1 handle ffff: ingress=
  • =tc filter add dev vlan1 parent ffff: protocol ip prio 50 u32 match ip src 0.0.0.0/0 police rate ${DOWNLINK}kbit burst 10k drop flowid :1=

-- Peti - 2006.12.10.