simulate-latency-pktloss-network-blog-header-960-2016

Linux mini-howto: Simulace zpoždění a ztrátovosti paketů v IP sítích

Linux mini-howto?
Krátce o nástrojích ze života sysadmina.

Simulace zpoždění a ztrátovosti paketů


V předchozím Linux-mini howto (Je tunelování TCP skrz TCP dobrý nápad ?) jsme na konci článku zmínili formou odkazů informace k této problematice i přes tento fakt a existenci Google přišlo pár dotazů jak na to, pojďme to v rychlosti proběhnout.

Diagnostika sítí je komplexní, rozsáhlá a složitá disciplína plná záludností, složitých software nástrojů a sofistikovaných drahých měřících přístrojů, přesto se hodí umět si vyrobit kontrolovaně svůj vlastní problém na stole (nemluvíme teď o “muchlání” a kroucení kabelů či nucení kolegů na chodbě běhat skrze bezdrátový spoj ;-) ) a to zcela zdarma díky Linux jádru.

Základní kvalitativní parametry sítě

Zpoždění (latency)
https://cs.wikipedia.org/wiki/Latence#V_informatice

Ztrátovost paketů (packet loss)
https://cs.wikipedia.org/wiki/Ztráta_paketů

Linux kernel, IP stack, fronty, netem NIC

Linux jádro má již od verze 2.6 (dobře, vlastně od 2.4.37) implementovanou funkcionalitu pro network emulaci (netem), pokud máte kernel 2.6 a novější, zakompilovanou podporu Network emulatoru do jádra či jako modul a k tomu nainstalovaný obslužný nástroj tc (traffic control) z balíku iproute2 máte vše potřebné.

Networking -->
   Networking Options -->
     QoS and/or fair queuing -->
        Network emulator

CONFIG_NET_SCH_NETEM=m
Jak funguje netem (linux network emulator) ?

Popišme si alespoň základní koncept síťování v Linux, jednoduchá ilustrace nám dobře pomůže k pochopení, na jedné straně máme aplikace komunikující prostřednictvím systémových volání jádra (syscall) s IP stackem, chtějí komunikovat s hostem a.b.c.d, navazovat spojení, posílat data a o moc více se nestarat.

simulate-latency-pktloss-network-linux-network-qdisc-2016

IP stack předává pakety do qdisc (Queueing discipline), což není nic jiného než scheduler (plánovač, kód v jádře systému) fronty, který rozhoduje o tom v jakém pořadí zpracovat pakety, každé výstupní rozhraní má alespoň jeden qdisc (scheduler, frontu), po průchodu frontou přes ovladač síťové karty putují data ven.

Výchozí fronta v Linuxu je většinou FIFO (First-in, First-Out).

root@netem-machine:~# tc -s qdisc
qdisc pfifo_fast 0: dev eth0 root refcnt 2 bands 3 priomap
 Sent 14757932 bytes 163954 pkt (dropped 0, overlimits 0 requeues 0)
 backlog 0b 0p requeues 0 

root@netem-machine:~# tc qdisc show
qdisc pfifo_fast 0: dev eth0 root refcnt 2 bands 3 priomap

Výpisem pomocí nástroje tc jsme si ověřili aktuálně nastavený qdisc na zařízení eth0 (ethernet), je patrné, že máme nastavenou frontu typu pfifo (packet FIFO) z následující obrázku je vidět, že pfifo interně implementuje i jednoduchý priority mapping a obsahuje uvnitř tři různé fronty pro přednostní zpracování.

pfifo qdisck

Pokud je fronta plná (linka je zatížená) a nemůže být přijmut další paket ke zpracování, dojde k jeho zahození, frontu lze pro tento účel zvětšit, následkem je většinou zvýšení latence, ale můžeme pozorovat i lepší využití linky.

Pro doplnění představ, v Linux jádře je dnes přes 20 různých schedulerů paketů (red, qfq, prio, mq, cbq, htb, sfq ,…) a nic Vám nebrání napsat si svůj vlastní unikátní pro Vaše potřeby. (http://lxr.free-electrons.com/source/net/sched/ )

Netem, skrze dlouhou okliku a vysvětlení qdisc se vraťme zpět k našemu emulátoru, z předchozích informací je zřejmé, že netem je implementován jako další plánovač paketů se schopností přidávat ke zpracování latence včetně náhodné distribuce, generovat ztrátovost, duplikovat i měnit pořadí paketů(reordering).

Použití je opravdu triviální, ukážeme si, nahrajeme modul netem do jádra, nastavíme na rozhraní eth0 qdisc netem, nastavíme parametry zpoždění, náhodného rozptylu, ztrátovosti a otestujeme.

root@netem-machine:~# modprobe sch_netem
root@netem-machine:~# tc qdisc show
qdisc pfifo_fast 0: dev eth0 root refcnt 2 bands 3 priomap
root@netem-machine:~# 
root@netem-machine:~# tc qdisc add dev eth0 root netem
root@netem-machine:~# tc qdisc show
qdisc netem 8004: dev eth0 root refcnt 2 limit 1000

Zpoždění 200ms , host 10.0.200.1 je součástí directly connected sítě, okamžitě lze vidět nárůst latence zde pomocí ping nástroje.

root@netem-machine:~# tc qdisc change dev eth0 root netem delay 200ms

root@test-machine:~$ ping 10.0.200.1
PING 10.0.200.1 (10.0.200.1) 56(84) bytes of data.
64 bytes from 10.0.200.1: icmp_seq=1 ttl=64 time=0.295 ms
64 bytes from 10.0.200.1: icmp_seq=2 ttl=64 time=0.268 ms
64 bytes from 10.0.200.1: icmp_seq=9 ttl=64 time=0.230 ms
64 bytes from 10.0.200.1: icmp_seq=10 ttl=64 time=200 ms
64 bytes from 10.0.200.1: icmp_seq=11 ttl=64 time=200 ms
64 bytes from 10.0.200.1: icmp_seq=12 ttl=64 time=200 ms

Zpoždění 500ms ± 100ms, je vidět zpoždění ± s velkým rozptylem (ne realtime jádro s tím asi dost bojuje).

root@test-machine:~# tc qdisc change dev eth0 root netem delay 500ms 100ms
root@test-machine:~$ ping 10.0.200.1
PING 10.0.200.1 (10.0.200.1) 56(84) bytes of data.
64 bytes from 10.0.200.1: icmp_seq=24 ttl=64 time=0.242 ms
64 bytes from 10.0.200.1: icmp_seq=25 ttl=64 time=0.272 ms
--- aplikace netem ---
root@test-machine:~$ ping 10.0.200.1
PING 10.0.200.1 (10.0.200.1) 56(84) bytes of data.
64 bytes from 10.0.200.1: icmp_seq=1 ttl=64 time=453 ms
64 bytes from 10.0.200.1: icmp_seq=2 ttl=64 time=479 ms
64 bytes from 10.0.200.1: icmp_seq=3 ttl=64 time=502 ms
64 bytes from 10.0.200.1: icmp_seq=4 ttl=64 time=491 ms
64 bytes from 10.0.200.1: icmp_seq=5 ttl=64 time=553 ms
64 bytes from 10.0.200.1: icmp_seq=6 ttl=64 time=532 ms
64 bytes from 10.0.200.1: icmp_seq=7 ttl=64 time=534 ms
64 bytes from 10.0.200.1: icmp_seq=8 ttl=64 time=490 ms
64 bytes from 10.0.200.1: icmp_seq=9 ttl=64 time=450 ms

Ztrátovost  10% (10% paketů bude náhodně zahazováno) + latence 100ms, náhodu lze ještě ovlivňovat korelací kdy je závislá na předchozím výsledku v %, je to trochu chaotické, ale vlastně méně chaotické, protože zmenšujeme náhodnost generátoru).

root@test-machine:~# tc qdisc change dev eth0 root netem delay 100ms loss 10%

root@test-machine:~$ ping -q -c 10 10.0.200.1
PING 10.0.200.1 (10.0.200.1) 56(84) bytes of data.
--- 10.0.200.1 ping statistics ---
10 packets transmitted, 9 received, 10% packet loss, time 9026ms
rtt min/avg/max/mdev = 100.222/100.298/100.371/0.163 ms

root@test-machine:~$ ping -q -c 10 10.0.200.1
PING 10.0.200.1 (10.0.200.1) 56(84) bytes of data.
--- 10.0.200.1 ping statistics ---
10 packets transmitted, 6 received, 40% packet loss, time 9013ms
rtt min/avg/max/mdev = 100.197/100.239/100.295/0.185 ms

Základní představu o konceptu netem a jeho využití jsme snad představili, studujte dokumentaci, další pěknou věcí, která stojí za vyzkoušení je poškozování paketů (netestovali jsme bohužel), není špatné si pro přehled projít i zdrojový kód netem, pěkné je nasazení netem i na příchozí provoz pomocí modulu IFB (pouze otočí in přes pseudo device na out a na něm udělat netem) v ukázkách dokumentace nejdete jak netem protáhnout i jen specifický traffic přes tc filter, omezit rychlost apod … prostě studujte.

V našem případě při testování a hledání problémů s TCP over TCP jsem si postavili script který měnil hodnoty zpoždení a ztrátovosti od malých až po extrémní, tak aby jsme donutili TCP prodlužovat adaptivní timeout a sledovat pomocí tcpdump/wireshark co se děje uvnitř spojení.

Děkuji za pozornost.
František Havel, MOJEservery.cz

Zdroje:

http://man7.org/linux/man-pages/man8/tc-netem.8.html

http://lxr.free-electrons.com/source/net/sched/sch_netem.c

http://www.linuxfoundation.org/collaborate/workgroups/networking/netem

http://info.iet.unipi.it/~luigi/dummynet/