====== Kształtowanie ruchu ====== ===== Wstęp ===== Kiedy pakiet jest gotowy do **wysłania**, trafia do "kolejki" ("queue"). [[https://pl.wikipedia.org/wiki/Plik:Netfilter-packet-flow-pl.svg|[1]]] \\ Kiedy system operacyjny jest gotowy do wysłania pakietu, pobiera go z "kolejki". "Kolejka" domyślnie jest kolejką – system wyciąga pakiety z kolejki w kolejności w jakiej zostały dodane. Można jednak zastąpić to zachowanie (dyscyplinę kolejkowania – Queueing DISCipline, qdisc) innym [[https://en.wikipedia.org/wiki/Network_scheduler|[2]]]. Pozwala to kształtować ruch – ograniczać prędkość i priorytetyzować wybrane pakiety ([[https://en.wikipedia.org/wiki/Traffic_shaping|Traffic shaping]]). Nie da się (w bezpośredni sposób) kształtować ruchu wchodzącego – karta sieciowa nie ma kontroli nad tym co i w jakiej kolejności ktoś do niej wysyła. ===== Materiały ===== * **[[http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/ts.html]]** * Rozdział 9 z: [[http://lartc.org/howto/index.html]] * [[http://linux-tc-notes.sourceforge.net/tc/doc/]] * [[http://luxik.cdi.cz/~devik/qos/htb/manual/userg.htm]] ===== Man / help ===== Pomoc do poleceń ''tc'' można uzyskać dodając (też w środek komendy) słowo kluczowe ''help'', np: tc qdisc help tc qdisc add dev eth0 handle 1 root codel help tc class add dev eth0 parent 2:2 classid 2:22 htb help tc filter add dev eth0 parent 5:0 u32 match ip sport 80 0xffff help Komenda ''tc'' opisana jest na wielu stronach podręcznika: * Główna strona: ''man tc'' * Przykłady stron: ''man tc-prio tc-htb tc-u32 tc-fw tc-tbf tc-fq_codel'' * Lista wszystkich dostępnych: ''whatis -r '^tc-.*' '' ===== Schemat polecenia ===== dodaje nową dyscyplinę typ dyscypliny | | | nazwa (numer) nowej dyscypliny | ^ ___L__ ^ / \ / \ / \ tc qdisc add dev eth0 handle 1 root htb default 2 tc qdisc add dev eth0 handle 2 parent 1:2 sfq divisor 4096 \______/ \______/ \________/ \__________/ | | | | | na urządzeniu eth0 | opcje konkretnej dyscypliny | | zajmuje się dyscyplinami kolejkowania określa do czego dołączona jest dyscyplina: • root – główna dla urządzenia • parent 1:2 – przyczepiona do klasy 1:2 ===== Proste przykłady ===== Wyświetlanie dyscyplin: * ''tc qdisc'' – dyscypliny dla wszystkich urządzeń * ''tc qdisc show dev eth0'' – podstawowy widok * ''tc -d qdisc show dev eth0'' – widok szczegółowy * ''tc -s qdisc show dev eth0'' – pokazuje statystyki //Zadanie 1. // Wyświetl jakie dyscypliny kolejkowe są używane. Sprawdź ile bajtów wysłał twój komputer przez kartę ''p4p1''. Usunięcie bieżącej dyscypliny kolejkowania: \\ ''tc qdisc del root dev eth0'' Dodanie przykładowych dyscyplin: * ''tc qdisc add dev eth0 handle 1 root pfifo'' * ''tc qdisc add dev eth0 handle 1 root sfq'' * ''tc qdisc add dev eth0 handle 1 root tbf rate 10mbit burst 64k latency 50ms'' * ''tc qdisc add dev eth0 handle 1 root cake bandwidth 10mbit'' Zawsze interfejs musi mieć jedną główną (root) dyscyplinę. Testowanie prędkości ([[https://hewlettpackard.github.io/netperf/|netperf]]): * instalacja netperf w opensuse: ''zypper install netperf'' * ''netserver -N'' – włącza działający w tle serwer oczekujący na klientów * ''netperf -H //host-docelowy//'' – nawiązuje połączenie z netserverem, uruchamia domyślny test (TCP_STREAM) * ''netperf -t UDP_STREAM -H //host-docelowy//'' – nawiązuje połączenie z netserverem, uruchamia test UDP_STREAM Testowanie prędkości ([[https://iperf.fr/|iperf3]]): * ''iperf3 -s'' – włącza działający w tle serwer oczekujący na klientów * ''iperf3 -c //host-docelowy//'' – nawiązuje połączenie z serwerem iperf3, uruchamia domyślny test (TCP) * ''iperf3 -ub0 -c //host-docelowy//'' – nawiązuje połączenie z serwerem iperf3, uruchamia test po UDP Testowanie prędkości - netcat i [[http://www.ivarch.com/programs/pv.shtml|pv]]: * instalacja pv w opensuse: ''zypper install pv'' * odbiorca: ''nc -l 3000 | pv > /dev/null'' * nadawca: ''pv /dev/zero | nc lab-net-1 3000'' Monitorowanie prędkości na wybranej dyscyplinie / klasie: * ''bmon'' [[https://github.com/tgraf/bmon]] //Zadanie 2a. // Zmień kolejkę na urządzeniu ''p4p1'' na podany wyżej przykładu z ''sfq''. Sprawdź maksymalną prędkość przesyłania danych. //Zadanie 2b. // Zmień kolejkę na urządzeniu ''p4p1'' na podany wyżej przykładu z ''tbf''. Sprawdź maksymalną prędkość przesyłania danych. Przepełnienie kolejki. ===== Bezklasowe i klasowe dyscypliny ===== Złożone dyscypliny kolejkowania pozwalają przyłączać do siebie **klasy** (class). \\ Administrator może wybrać jaki ruch trafia do konkretnej klasy tworząc **filtry** (filter). Proste dyscypliny kolejkowania nie pozwalają na to – stąd podział na klasowe i bezklasowe qdisc. === Bezklasowe === Przykładowe: * ''pfifo'' i ''bfifo'' – proste kolejki o rozmiarze określonym w pakietach / bajtach * ''pfifo_fast'' – kolejki priorytetowe dające pierwszeństwo na podstawie pola ToS protokołu IP [[http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/ts.html#pfifo-fast]] * ''red'' – zmusza połączenia do spowolnienia zanim zapchają kolejkę, odrzucając losowo pakiety kiedy w kolejce jest ich odpowiednio dużo [[https://www.ciscopress.com/articles/article.asp?p=352991&seqNum=8]] * ''sfq'' – zapewnia fairness [[http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/ts.html#stochastic-fair-queueing-sfq]] * ''tbf'' – ogranicza prędkość [[http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/ts.html#token-bucket-filter]] * ''fq_codel'', ''codel'' – dobiera rozmiar kolejki tak żeby nie ograniczać prędkości i utrzymywać małe opóźnienie [[http://www.bufferbloat.net/projects/codel/wiki]] * ''cake'' – łączy wiele pomysłów tak, by działać sensownie w typowych zastosowaniach, m. inn. priorytety, wydzielanie połączeń (flows) i inna obsługa flows o różnej charakterystyce, fairness, możliwość ograniczenia przepustowości [[https://www.bufferbloat.net/projects/codel/wiki/CakeTechnical/]] === Klasowe === Przykładowe: * ''prio'' [[http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/ts.html#prio]] * ''htb'' [[http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/ts.html#hierarchical-token-bucket]] ===== qdisc vs class ===== Klasowy qdisc moze mieć wiele klas. \\ qdisc zawsze ma numer ''//x//:0'' – minor ''0'' i dowolny major. \\ class ma numer ''//x//://y//'', gdzie major oznacza qdisc w którym został stworzony, a minor – niezerowy identyfikator klasy wewnątrz qdisc Zapis ''1:0'' mozna zastąpić czasami przez ''1:'' (a czasami też samym ''1''). ===== prio + filtry ===== ==== prio ==== **[[http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/ts.html#prio]]** ''tc qdisc add dev //eth0// handle //1// root prio'' – ustawienie klasowego qdisc, kolejki priorytetowej ''tc class show dev //eth0//'' – wyświetlenie klas (prio automatycznie tworzy 3 klasy) \\ ''tc -s class show dev //eth0//'' – wyświetlenie statystyk klas //Zadanie 3. // Zmień dyscyplinę kolejkową na ''p4p1'' na ''prio''. Wyświetl listę klas i ich statystyki. Zbadaj gdzie domyślnie trafia ruch (patrząc na statystyki lub używając ''bmon''). ==== Filtry ==== Pakiet który trafia do "kolejki" pakietów czekających na wysłanie trafia wpierw do root qdisc. \\ Ten decyduje co z nim zrobić: przegląda filtry i jeśli któryś pasuje – stosuje go. Jeśli żaden nie pasuje – wykonuje domyślną akcję. \\ Np. dla ''prio'' domyślna akcja to wybór jednej z 3 kolejek bazując na polu TOS w nagłówku IP (''man tc-prio''). Polecenie ''tc filter show dev //eth0//'' listuje filtry. \\ //Uwaga:// samo ''tc filter show'' zwróci pusty wynik nawet jeśli dodano filtry. Więcej informacji o filtrach: http://www.lartc.org/howto/lartc.qdisc.filters.html === Filtr ''u32'' === ++++ Filtr u32 | * Przykładowy filtr: \\ ''tc filter add dev eth0 parent 1:0 protocol ip u32 match ip dst 150.254.32.130/32 flowid 1:3'' dodaj na klasie/dycyplinie /-\ /-------\ tc filter add dev eth0 parent 1:0 protocol ip u32 match ip dst 150.254.32.130/32 flowid 1:3 \_____/ \____/ \_______/ \______________________________________________/ filtry na urządzeniu filtr ma dotyczyć IP filtr 'u32' dopasowujący się do IP wymuszający przydzielenie do klasy 1:3 * Przetestuj działanie filtra komendą \\ ''netperf -H 150.254.32.130 & netperf -H 150.254.32.131 ; wait'' * Przetestuj działanie filtra komendą \\ ''netperf -t UDP_STREAM -H 150.254.32.130 & netperf -t UDP_STREAM -H 150.254.32.131 ; wait'' * Wypisz statystyki dla klas, wygeneruj ruch, wypisz ponownie statystyki i porównaj \\ ''tc -s class show dev eth0'' \\ Możesz też skorzystać z programu ''bmon'' do monitorowania na bieżąco przepustowości dla klas \\ (żeby zainstalować ''bmon'', użyj polecenia ''zypper install bmon'') * Filtr ''u32'' w ''tc'' pozwalają filtrować: * adresy IP, źródłowe i docelowe * protokoły warstwy transportowej (icmp, tcp, udp) ++ przykład|: \\ ''tc filter add dev eth0 parent 1:0 u32 match ip protocol 1 0xff flowid 1:1''++ * pole TOS w nagłówku IP (https://en.wikipedia.org/wiki/Differentiated_services) * porty protokołów warstwy transportowej (ssh, dns, http, openvpn, xmpp) ++ przykład | (nie do końca poprawny): \\ ''tc filter add dev eth0 parent 1:0 u32 match ip sport 443 0xffff match ip protocol 6 0xff flowid 1:1''++ //Zadanie 4a. // Dodaj filtr typu ''u32'' który pakiety do wybranego komputera będzie umieszczał w klasie o niższym priorytecie. Korzystając ze statystyk klas sprawdź czy filtr działa. Sprawdź (np. netperfem) jakie są prędkości wysyłania danych jeśli jednocześnie dane są wysyłane tego komputera i do dowolnego innego. ++++ === Filtr ''fw'' === * W jądrze Linuksa z każdym pakietem jest powiązanych kilka znaczników (MARK, CONNMARK, SECMARK). * Wartość znaczników można ustawiać (i sprawdzać) w filtrach iptables / nft, np: \\ ''iptables -A OUTPUT -d 150.254.32.130 -j MARK --set-mark 0x130'' \\    – dodaje znacznik 0x130 do pakietów idących do adresu 150.254.32.130 * Filtr ''fw'' dodany do qdisc kieruje do wybranej klasy pakiety ze znacznikiem MARK pasującym do podanego ''handle'', np: \\ ''tc filter add dev eth0 parent 1:0 handle 0x130 fw flowid 1:3'' \\    – nakazuje pakiety zaznaczone jako 0x130 wrzucać do klasy 1:3 \\
           dodaj             na qdisc/klasie             wybór filtra fw  
           \---/              \----------/                    \--/
tc filter   add   dev eth0     parent 1:0     handle 0x130     fw      flowid 1:3
\______/         /________\                  /____________\           /__________\
 filtry         na urządzeniu             identyfikator używany      umieszcza pasujące
                                          jednocześnie jako          pakiety w klasie 1:3
                                          szukana wartość MARK
* Wartość ''handle'' może być też podana jako wartość z maską, np: \\ ''tc filter add dev eth0 parent 1:0 handle 0x30/0xf0 fw flowid 1:3'' \\ pozwala to na wykorzystywanie jedynie części znacznika MARK w tc (i umożliwia wykorzystanie reszty np. w filtrowaniu) //Zadanie 4b. // Dodaj filtr typu ''fw'' i regułę iptables tak żeby pakiety idące wybranego komputera były umieszczane w klasie o niższym priorytecie. Korzystając ze statystyk klas sprawdź czy ustawienia działają. Sprawdź (np. netperfem) jakie są prędkości wysyłania danych jeśli jednocześnie dane są wysyłane tego komputera i do dowolnego innego. //Zadanie 5. // Dodaj filtr typu ''fw'' i regułę iptables które pakiety protokołu icmp będą umieszczały w kolejce o najwyższym priorytecie. Korzystając ze statystyk klas sprawdź czy filtr działa. ===== htb ===== **[[http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/ts.html#hierarchical-token-bucket]]** ++++ Gotowa konfiguracja htb (do użycia na laboratoriach w przypadku braku czasu)| tc qdisc del dev em1 root tc qdisc add dev em1 root handle 1 htb default 32 tc class add dev em1 parent 1:0 classid 1:1 htb rate 100mbit tc class add dev em1 parent 1:1 classid 1:2 htb rate 60mbit tc class add dev em1 parent 1:2 classid 1:21 htb rate 20mbit ceil 60mbit tc class add dev em1 parent 1:2 classid 1:22 htb rate 40mbit ceil 60mbit tc class add dev em1 parent 1:1 classid 1:3 htb rate 40mbit ceil 100mbit tc class add dev em1 parent 1:3 classid 1:31 htb rate 30mbit ceil 100mbit tc class add dev em1 parent 1:3 classid 1:32 htb rate 10mbit ceil 50mbit tc filter add dev em1 parent 1:0 handle 0x21 fw flowid 1:21 tc filter add dev em1 parent 1:0 handle 0x22 fw flowid 1:22 tc filter add dev em1 parent 1:0 handle 0x31 fw flowid 1:31 iptables -F iptables -A OUTPUT -d lab-net-X -j MARK --set-mark 0x21 iptables -A OUTPUT -d lab-net-Y -j MARK --set-mark 0x22 iptables -A OUTPUT -d lab-net-Z -j MARK --set-mark 0x31 ++++ Dodanie dyscypliny kolejkowej htb realizuje się przykładowo komendą: tc qdisc add dev eth0 root handle 1 htb default 2 gdzie ''//default 2//'' oznacza, że domyślnie pakiety będą trafiać do klasy 1:2 HTB nie tworzy domyślnie żadnych klas. Tworzenie trzech klas: tc class add dev eth0 parent 1:0 classid 1:1 htb rate 100mbit tc class add dev eth0 parent 1:1 classid 1:2 htb rate 50mbit tc class add dev eth0 parent 1:1 classid 1:3 htb rate 40mbit ceil 100mbit Usunięcie klasy: ''tc class del dev eth0 classid 1:3'' W HTB zwykle tworzy się klasy tak, by bezpośrednio do qdisc była podpięta jedna klasa, definiująca tylko rate (maksymalny dla łącza), a pod nią były podpięte kolejne, definiujące rate i ceil (których sumaryczny rate ani pojedynczy ceil jest nie większy niż możliwości łącza). Klasy w HTB mogą tworzyć dowolną hierarchię (drzewo) – można wpinać klasy do klas, np: tc class add dev eth0 parent 1:1 classid 1:4 htb rate 10mbit ceil 100mbit tc class add dev eth0 parent 1:4 classid 1:41 htb rate 5mbit ceil 50mbit tc class add dev eth0 parent 1:4 classid 1:42 htb rate 5mbit ceil 100mbit [[http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/ts.html#przyklad]] //Zadanie 6a. // Zmień dyscyplinę kolejową na ''htb''. Następnie narysuj i stwórz odpowiednimi poleceniami drzewo klas potrzebne do podzielenia ruchu tak, by: * na pakiety idące do jednego wybranego komputera było zagwarantowane 10mbit bez możliwości pożyczania ruchu * na pakiety idące do drugiego wybranego komputera było zagwarantowane 20mbit ruchu z możliwością pożyczania do 100mbit * na pozostałe pakiety było zagwarantowane 70mbit ruchu z możliwością pożyczania do 100mbit //Zadanie 6b. // Dodaj odpowiednie filtry i przetestuj czy działają korzystając ze statystyk klas. //Zadanie 6c. // Sprawdź przestrzeganie limitów i działanie pożyczania używając programu netperf / iperf / nc+pv ===== Dodawanie qdisc do klas ===== Każda klasa która nie ma żadnej klasy pod sobą ma swój domyślny qdisc. \\ Można go zmienić na dowolny wybrany, np: \\ ''tc qdisc add dev eth0 handle 2 parent 1:2 fq_codel'' \\ ''tc qdisc add dev eth0 handle 3 parent 1:42 sfq'' Usunięcie qdisc: \\ ''tc qdisc del dev eth0 handle 2:0 parent 1:2'' [[http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/ts.html#koleki-klasowe-zlozone]] Można w ten sposób dowolnie zagnieżdżać w sobie klasowe dyscypliny kolejkowania. //Zadanie 7. // Dodaj pod wybraną klasę z utworzonej wcześniej dyscypliny kolejkowej kolejną dyscyplinę kolejkową dowolnego typu. ===== [Ekstra] IFB + ingress tc ===== Karta sieciowa nie ma kontroli nad tym co i w jakiej kolejności ktoś do niej wysyła, ale używając TC można próbować kształtować ruch wejściowy opóźniając lub odrzucając to co już przyszło. Ma to sens, bo protokół TCP dostosowuje prędkość nawiązanego już połączenia do możliwości sieci, więc opóźnienie lub odrzucenie segmentów TCP które przyszły do systemu spowodują zmianę prędkości wsyłania przez nadawcę. TC pozwala na bezpośrednie kształtowanie ruchu wchodzącego, ale jest ono zdecydowanie mniej rozbudowane niż kształtowanie ruchu wychodzącego. Dlatego zwykle używa się urządzenia pośredniego, które pozwala traktować ruch wchodzący jako wychodzący. ++++ Przykład kształtowania na wejściu: | # dodanie urządzenia 'pośredniego' (Intermediate Functional Block), które: # * działa jako kolejka na wejściu # * pozwala kształtować wysyłanie pakietów z kolejki do systemu # (https://wiki.linuxfoundation.org/networking/ifb) ip link add name ifb0 type ifb ip link set ifb0 up # # alternatywnie można użyć komend: #modprobe ifb numifbs=1 #ifconfig ifb0 up # dodanie dyscypliny kolejkowej typu ingress - pakiety wchodzące # (handle ffff jest domyślne dla qdisc ingress) tc qdisc add dev enp0s3 handle ffff ingress # dyscyplina ingress nie pozwala na rozbudowaną konfigurację, ale: # można przekazać ruch do pośredniego urządzenia, które pozwoli ruchu wchodzący przetwarzać tak jak wychodzący # dodanie flitra, który cały ruch wchodzący przekaże do urządzenia ifb: # # na urządzeniu enp0s3 sprawdza czy 0==0 jako ruch wychodzący na ifb0 # ___/\___ ___/\__________ _/\_ __/\__ # / \ / \ / \ / \ tc filter add dev enp0s3 parent ffff: u32 match u32 0 0 action mirred egress redirect dev ifb0 # \____ ____/ \____ _____/ \_ ___/ # \/ \/ \/ # na qdisc ffff: (ingres) przekierowuje ruch (man tc-mirred) # efekt powyższej komendy widać po wpisaniu tc filter show dev enp0s3 ingress # lub: #tc filter show dev enp0s3 parent ffff: # dalsza konfiguracja odbywa się jak dla zwykłego ruchu wychodzącego dla urządzenia ifb # przykład z HTB: tc qdisc add dev ifb0 root handle 1 htb default 2 tc class add dev ifb0 parent 1:0 classid 1:1 htb rate 1Gbit tc class add dev ifb0 parent 1:1 classid 1:2 htb rate 100Mbit ceil 1Gbit tc class add dev ifb0 parent 1:1 classid 1:3 htb rate 100Mbit tc filter add dev ifb0 parent 1:0 protocol ip u32 match ip src 10.0.1.3 flowid 1:3 ++++ ===== [Ekstra] netem ===== Netem to dyscyplina kolejkowa pozwalająca symulować opóźnienia, błędy, stratę i duplikację pakietów. Dzięki netem można w łatwy sposób sprawdzać jak zachowałby się dany program w zadanych warunkach, np. przy dużych opóźnieniach (łącza satelitarne), czasowo pojawiającym się opóźnieniu kilku pakietów (internet po sieciach radiowych), zmianie kolejności pakietów (multipath routing w infrastrukturze ISP). \\ Dla przykładu: podczas zajęć z programowania netem [[sk2:sockets_caveats#kolejnosc_danych_i_nie_zawodnosc|był użyty]] do sprawdzenia jakie gwarancje daje TCP, a nie daje ich UDP. Pomoc do netem można znaleźć: * ''man tc-netem'' * [[https://wiki.linuxfoundation.org/networking/netem]]