Kształtowanie Ruchu i Zaawansowany Routing HOWTO Bert Hubert Netherlabs BV <[1]bert.hubert%netherlabs.nl> Thomas Graf (Autor sekcji) <[2]tgraf%suug.ch> Gregory Maxwell (Autor sekcji) Remco van Mook (Autor sekcji) <[3]remco%virtu.nl> Martijn van Oosterhout (Autor sekcji) <[4]kleptog%cupid.suninternet.com> Paul B Schroeder (Autor sekcji) <[5]paulsch%us.ibm.com> Jasper Spaans (Autor sekcji) <[6]jasper%spaans.ds9a.nl> Pedro Larroy (Autor sekcji) <[7]piotr%omega.resa.es> Polskie tłumaczenie: Łukasz Bromirski <[8]lbromirski%mr0vka.eu.org> Bardzo praktyczne podejście do iproute2, kontroli ruchu oraz po części do netfilter. _________________________________________________________________ Spis treści 1. [9]Dedykacja 2. [10]Wprowadzenie [11]Disclaimer & Licencja [12]Wymagana wiedza [13]Co Linux może zrobić dla Ciebie [14]Notatki gospodyni [15]Dostęp, CVS i wysyłanie uaktualnień [16]Lista pocztowa [17]Układ tego dokumentu 3. [18]Wprowadzenie do iproute2 [19]Dlaczego iproute2? [20]Przewodnik po iproute2 [21]Wymagania wstępne [22]Badanie obecnej konfiguracji [23]ip pokazuje nam połączenia [24]ip pokazuje nam nasze adresy IP [25]ip pokazuje nam trasy routingu [26]ARP 4. [27]Reguły - baza danych polityki routingu [28]Prosty routing na podstawie źródła (ang. source routing) [29]Routing dla wielu łącz/do wielu dostawców usług [30]Dostęp rozłączny (ang. split access) [31]Rozkładanie obciążenia (ang. load balancing) 5. [32]Tunele - GRE i inne [33]Parę ogólnych uwag o tunelach: [34]Tunelowanie IP w IP [35]Tunelowanie GRE [36]Tunelowanie IPv4 [37]Tunelowanie IPv6 [38]Tunele w przestrzeni użytkownika 6. [39]Tunelowanie IPv6 z Cisco i/lub 6bone [40]Tunelowanie IPv6 7. [41]IPsec: bezpieczne IP przez Internet [42]Wprowadzenie do Ręcznej Wymiany Kluczy [43]Automatyczna wymiana kluczy [44]Teoria [45]Przykład [46]Automatyczna wymiana kluczy przy użyciu certyfikatów X.509 [47]Tunele IPsec [48]Inne oprogramowanie IPsec [49]Współpraca IPsec z innymi systemami [50]Windows 8. [51]Routing multicastowy 9. [52]Dyscypliny kolejkowania dla Zarządzania Pasmem [53]Kolejki i Dyscypliny Kolejkowania wyjaśnione [54]Proste, bezklasowe Dyscypliny Kolejkowania [55]pfifo_fast [56]Token Bucket Filter [57]Sprawiedliwe Kolejkowanie Stochastyczne (ang. "Stochastic Fairness Queueing") [58]Kiedy używać której kolejki [59]Terminologia [60]Dyscypliny kolejkowania z klasami [61]Przepływ w qdisc z klasami [62]Rodzina kolejek z klasami: korzenie, uchwyty, rodzeństwo i rodzice [63]qdisc PRIO [64]Sławna qdisc CBQ [65]Hierarchiczne Wiadro Żetonów (ang. "Hierarchical Token Bucket") [66]Klasyfikowanie pakietów filtrami [67]Trochę prostych przykładów filtrowania [68]Wszystkie komendy filtrujące, których będziesz normalnie potrzebował [69]Pośrednie urządzenie kolejkujące (ang. "The Intermediate queueing device", IMQ [70]Przykładowa konfiguracja 10. [71]Rozkładanie obciążenia na wiele interfejsów [72]Problemy [73]Inne możliwości 11. [74]Netfilter & iproute - oznaczanie pakietów 12. [75]Zaawansowane filtry i (re-)klasyfikowanie pakietów [76]Klasyfikator u32 [77]Selektor U32 [78]Selektory ogólne [79]Selektory specyficzne [80]Klasyfikator route [81]Filtry określające politykę [82]Sposoby na określenie polityki [83]Akcje do podjęcia przy przekroczeniu pasma [84]Przykłady [85]Filtry mieszające i bardzo dużo szybkiego filtrowania [86]Filtrowanie ruchu IPv6 [87]Dlaczego filtry tc dla IPv6 nie działają? [88]Oznaczanie pakietów IPv6 przez ip6tables [89]Zastosowanie selektora u32 do testu pakietu IPv6 13. [90]Parametry sieciowe kernela [91]Filtrowanie trasy powrotnej [92]Mało znane ustawienia [93]Podstawowe IPv4 [94]Ustawienia dotyczące urządzeń [95]Polityka dotycząca sąsiadów [96]Ustawienia dla routingu 14. [97]Zaawansowane i mniej znane kolejki z dyscyplinami [98]bfifo/pfifo [99]Parametry i użycie [100]Algorytm Clarka-Shenkera-Zhanga (CSZ) [101]DSMARK [102]Wprowadzenie [103]Z czym jest związany Dsmark? [104]Wskazówki dotyczące Usług Zróżnicowanych [105]Praca z Dsmark [106]Jak działa SCH_DSMARK. [107]Filtr TC_INDEX [108]Przychodzące qdisc [109]Parametry i użycie [110]Losowe Wczesne Wykrywanie (ang. "Random Early Detection", RED) [111]Ogólne Losowe Wczesne Wykrywanie (ang. "Generic Random Early Detection", GRED) [112]Emulacja VC/ATM [113]Ważony Round Robin (ang. "Weighted Round Robin", WRR) 15. [114]Książka kucharska [115]Praca z wieloma lokalizacjami z różnymi SLA [116]Ochrona komputera przed powodziami SYN [117]Ograniczenie częstotliwości ICMP by zapobiec atakom DDoS [118]Priorytetyzacja ruchu interaktywnego [119]Przezroczyste cache przy użyciu netfilter, iproute2, ipchains i squida [120]Diagram przepływu ruchu po implementacji [121]Unikanie problemów z rozpoznaniem przez MTU trasy a ustawienia MTU dla tras [122]Rozwiązanie [123]Unikanie problemów z rozpoznaniem przez MTU trasy a Zmniejszanie MSS (dla użytkowników kablowych ADSL, PPPoE i PPPtP) [124]Najlepszy `udrażniacz' dla ruchu sieciowego: małe opóźnienia, szybkie wrzucanie i ściąganie [125]Dlaczego nie działa to najlepiej w domyślnej konfiguracji [126]Skrypt (CBQ) [127]Skrypt (HTB) [128]Ograniczanie ruchu dla pojedyńczego hosta lub podsieci [129]Przykład rozwiązania z QoS i NATem [130]Zoptymalizujmy cenne pasmo [131]Klasyfikacja pakietów [132]Ulepszanie naszej konfiguracji [133]Niech to wszystko dzieje się samo przy starcie 16. [134]Budowanie mostów i pseudo-mostów z Proxy ARP [135]Stan mostkowania i iptables [136]Mostkowanie i kształtowanie ruchu [137]Pseudo-mosty i Proxy-ARP [138]ARP & Proxy-ARP [139]Skonfigurujmy to! 17. [140]Routing dynamiczny - OSPF i BGP [141]Setting up OSPF with Zebra [142]Założenia [143]Konfiguracja Zebry [144]Uruchomienie Zebry [145]Konfiguracja BGPv4 w Zebrze [146]Topologia przykładowej sieci [147]Przykładowa konfiguracja [148]Sprawdzanie konfiguracji 18. [149]Inne możliwości 19. [150]Dalsza lektura 20. [151]Podziękowania 21. [152]Podziękowania dla polskiego tłumaczenia _________________________________________________________________ Rozdział 1. Dedykacja Dokument ten dedykowany jest całemu mnóstwu ludzi i jest moją próbą dania czegoś w zamian. By wymienić zaledwie parę osób: * Rusty Russell * Aleksiej N. Kuzniecow * dobrzy ludzie z Google * Załoga Casema Internet _________________________________________________________________ Rozdział 2. Wprowadzenie Witam, Szanowny Czytelniku. Ten dokument ma nadzieję rozjaśnić trochę zagadnienia routingu w Linuksie 2.2/2.4. Większość użytkowników nie zdaje sobie nawet sprawy, że posługuje się narzędziami, które potrafią naprawdę spektakularne rzeczy. Komendy takie jak route czy ifconfig są zaledwie namiastkami rozbudowanej i potężnej infrastrukturę iproute2. Mam nadzieję, że to HOWTO będzie tak czytelne jak te napisane przez Rusty Russell'a z zespołu netfilter. Możesz zawsze skontaktować się z nami, pisząc na adres [153]zespołu HOWTO. Prosimy jednak byś rozważył wysłanie postu na listę pocztową (zajrzyj do odpowiedniej sekcji) jeśli masz pytania nie dotyczące bezpośrednio tego HOWTO. Nie stanowimy darmowego helpdesku, ale zwykle odpowiadamy na pytania zadawane na liście. Zanim zgubisz się czytając ten dokument, a wszystko co chcesz robić to kształtowanie ruchu, pomiń wszystko i przejdź bezpośrednio do rozdziału [154]Other possibilities by zapoznać się z CBQ.init. _________________________________________________________________ Disclaimer & Licencja Dokument ten rozpowszechniany jest w nadziei, że będzie użyteczny, ale BEZ ŻADNEJ GWARANCJI; nawet bez implikowanej gwarancji RĘKOJMI lub PRZYDATNOŚCI DO KONKRETNEGO ZASTOSOWANIA. Krótko mówiąc, jeśli twoja sieć STM-64 padnie albo posłuży do rozesłania pornografii do najbardziej cenionych klientów - to nie była nasza wina. Przykro nam. Wszystkie prawa zastrzeżone (c) 2001 przez bert hubert, Gregory Maxwell, Martijn van Oosterhout, Remco can Mook, Paul B. Schroeder i inni. Materiał ten może być dystrybuowany tylko na zasadach określonych w Open Publication License, v1.0 lub późniejszej (ostatnia wersja jest obecnie dostępna pod adresem http://www.opencontent.org/openpub/). Namawiamy do darmowego kopiowania i dystrybuowania (sprzedawania lub rozdawania) tego dokumentu w dowolnym formacie. Wymagamy jednak by poprawki i/lub komentarze przekazywać do koordynatora dokumentu. Wymagamy również, żeby w przypadku publikacji tego HOWTO w wersji drukowanej, autorzy otrzymali próbki na "potrzeby recenzji" :-). _________________________________________________________________ Wymagana wiedza Tak jak wskazuje tytuł, to "Zaawansowane" HOWTO. O ile nie oznacza to w żadnym przypadku wiedzy z dziedziny technologii rakietowych, zakładamy że posiadasz pewną wiedzę. Poniżej trochę odwołań, które mogą pomóc w nauczeniu się czegoś: [155]Zagadnienia sieciowe HOWTO, Rusty Russell'a Bardzo ładne wprowadzenie, wyjaśniające co to jest sieć i jak łączy się z innymi sieciami. Sieć Linuksa (poprzednio Net-3 HOWTO) Doskonała rzecz, choć bardzo szczegółowa. Uczy wielu rzeczy, które są już skonfigurowane jeśli możesz połączyć się z Internetem. Powininno znajdować się w /usr/doc/HOWTO/NET3-4-HOWTO.txt, ale można je również znaleźć pod adresem [156]online. _________________________________________________________________ Co Linux może zrobić dla Ciebie Krótka lista rzeczy, które można uzyskać: * kształtować pasmo sieciowe (ang. bandwidth) dla określonych komputerów * kształtować pasmo sieciowe do określonych komputerów * pomóc ci sprawiedliwie dzielić twoje pasmo sieciowe * bronić sieć przed atakami typu DoS * bronić Internet przed twoimi klientami * wykorzystywać wiele serwerów jak jeden, by uzyskać równoważenie obciążenia i zwiększoną dostępność * ograniczyć dostęp do twoich komputerów * ograniczyć dostęp twoich użytkowników do innych komputerów * prowadzić routing w oparciu o identyfikator użytkownika (tak!), adres MAC, źródłowy adres IP, port, typ usługi, czas dnia i zawartość Obecnie niewiele osób używa tych zaawansowanych możliwości. Dzieje się tak z wielu powodów. O ile dokumentacja jest bardzo szczegółowa, nie jest zbyt przyjazna czy zrozumiała. A kształtowanie ruchu jest prawie w ogóle nieudokumentowane. _________________________________________________________________ Notatki gospodyni Warto zaznaczyć parę rzeczy dotyczących tego dokumentu. Mimo, że jestem autorem większości teksu, nie chcę by tak zostało. Jestem wielkim zwolennikiem ruchu Open Source, więc zachęcam do nadsyłania przemyśleń, uaktualnień, poprawek i tak dalej. Nie zwlekajcie z informowaniem mnie o literówkach lub po prostu błędach. Jeśli mój Angielski jest trochę drewniany, proszę zwrócić uwagę, że nie jest to mój język ojczysty. Zapraszam do nadsyłania sugestii. Jeśli uważasz, że masz lepsze kwalifikacje by zajmować się którąś sekcją, lub myślisz że mógłbyś napisać a potem zajmować się nowymi sekcjami, zapraszam do zrobienia tego. Źródło tego dokumentu dostępne jest przez CVS w formacie SGML. Namawiam do dołączenia się do projektu. By wam pomóc, znajdziecie tu wiele uwag FIXME. Poprawki są zawsze mile widziane! Za każdym razem gdy znajdziesz notatkę FIXME, powinieneś wiedzieć, że wchodzisz na nieznane terytorium. Nie chodzi o to, że wszędzie będą błędy, ale należy być uważnym. Jeśli udało Ci się coś potwierdzić lub wręcz przeciwnie - wskazać błąd, proszę, daj nam znać byśmy mogli popracować nad fragmentem zawierającym notkę FIXME. W tym HOWTO pozwolę sobię na pewną swobodę. Na przykład, zakładam że posiadasz 10Mbit-owe połączenie z Internetem, mimo że nie jest to zbyt popularna konfiguracja. _________________________________________________________________ Dostęp, CVS i wysyłanie uaktualnień To HOWTO znajduje się pod [157]tym adresem. Na potrzeby projektu utrzymujemy serwer CVS z anonimowym dostępem z całego świata. Jest to przydatne z wielu powodów. Uaktalnienie twojej kopii do najnowszej wersji czy też wysłanie nam poprawek nie stanowi dzięki takiemu rozwiązaniu żadnego problemu. Co więcej, system ten umożliwia wielu autorom na pracę nad tekstem źródłowym niezależnie. $ export CVSROOT=:pserver:anon@outpost.ds9a.nl:/var/cvsroot $ cvs login CVS password: [wprowadź 'cvs' (bez apostrofów)] $ cvs co 2.4routing cvs server: Updating 2.4routing U 2.4routing/lartc.db Jeśli zauważysz błąd, lub chciałbyś coś dodać, popraw to na swojej lokalnej kopii, następnie uruchom cvs -z3 diff -uBb i wyślij rezultat pod adres <[158]howto%ds9a.nl> - łatwiej będzie nam się nim zająć. Dziękujemy! Przy okazji, upewnij się że edytowałeś plik .db - pozostałe są z niego generowane. Razem z dokumentem źródłowym dostępny jest również plik Makefile, który powinien pomóc Ci wygenerować dokumenty w formacie postscript, dvi, pdf, html i czystym tekście. Być może będziesz musiał zainstalować docbook, docbook-utils, ghostscript oraz tetex by otrzymać wszystkie formaty. Nie edytuj pliku 2.4routing.sgml! Zawiera starszą wersję tego HOWTO. Obecny plik zawierający HOWTO to lartc.db. _________________________________________________________________ Lista pocztowa Autorzy otrzymują rosnącą liczbę poczty dotyczącej tego HOWTO. Z uwagi na interes ogółu, zdecydowaliśmy o stworzeniu listy pocztowej, na której ludzie mogą dyskutować między sobą o zagadnieniach zaawansowanego routingu i kształtowania pasma. Możesz zapisać się na tą listę pod [159]tym adresem. Należy zwrócić uwagę, że autorzy są bardzo powściągliwi jeśli chodzi o odpowiadanie na pytania nie zadane na liście. Chcielibyśmy ją archiwizować i utrzymywać jako swego rodzaju bazę wiedzy. Jeśli masz pytanie, proszę przeszukaj archiwum, a następnie wyślij post na listę. _________________________________________________________________ Układ tego dokumentu Zaczniemy robić interesujące rzeczy praktycznie zaraz, co oznacza, że na początku wiele rzeczy możesz uznać za słabo lub wcale nie wytłumaczone. Przeczytaj je zakładając, że wszystko stanie się jasne potem. Routing i filtrowanie to dwie różne rzeczy. Filtrowanie zostało bardzo dobrze udokumentowane w HOWTO Rusty'ego, które dostępne są pod adresem: * [160]Rusty's Remarkably Unreliable Guides My skupimy się głównie na pokazaniu co możliwe jest przy połączeniu infrastruktury netfilter i iproute2. _________________________________________________________________ Rozdział 3. Wprowadzenie do iproute2 Dlaczego iproute2? Większość dystrybucji Linuksa, i większość UNIXów, używa szacownych komend arp, ifconfig i route. O ile narzędzia te działają, powodują trochę nieoczekiwanych rezultatów od wersji Linuksa 2.2 w górę. Na przykład, tunele GRE są integralną częścią routingu a wymagają oddzielnych narzędzi. Jeśli chodzi o iproute2, tunele są integralną częścią zestawu. Kernele Linuksa od wersji 2.2 zawierają kompletnie przeprojektowany podsystem sieciowy. Wydajność nowego kodu i zestaw jego możliwości powoduje, że Linuks nie ma zbyt wielu konkurentów na arenie systemów operacyjnych. Tak naprawdę, nowy kod routujący, filtrujący i klasyfikujący jest potężniejszy niż ten dostarczany w większości dedykowanych routerów, ścian ogniowych i produktów zajmujących się kształtowaniem pasma. W trakcie rozwijania nowych koncepcji sieciowych, ludzie znajdowali zawsze sposoby by dołożyć je do istniejących szkieletów już stworzonych systemów operacyjnych. To ciągłe dodawanie kolejnych warstw doprowadziło do tego, że kod sieciowy może czasami ciekawie się zachowywać lub wyglądać, tak jak to ma miejsce w przypadku większości ludzkich języków. W przeszłości, Linux emulował obsługę większości zagadnień sieciowych w sposób zgody z SunOS, który sam nie jest zbyt idealny. Nowy szkielet umożliwia jasne wyrażanie potrzeb i pragnień niedostępnych wcześniej w Linuksie. _________________________________________________________________ Przewodnik po iproute2 Linuks posiada bardzo wyrafinowany system kształtowania pasma, nazywany Traffic Control. System ten udostępnia wiele metod klasyfikowania, priorytetowania, współdzielenia i ograniczania zarówno ruchu wychodzącego jak i przychodzącego. Zaczniemy od krótkiej wycieczki po możliwościach iproute2. _________________________________________________________________ Wymagania wstępne Powinieneś upewnić się, że wszystkie narzędzia z przestrzeni użytkownika są zainstalowane. Paczka nazywa się 'iproute' zarówno w przypadku RedHat'a jak i Debiana, można ją znaleźć pod adresem ftp://ftp.inr.ac.ru/ip-routing/iproute2-2.2.4-now-ss??????.tar.gz". Możesz również spróbować pobrać i użyć najnowszą wersję, znajdującą się [161]pod tym adresem Niektóre części iproute wymagają byś włączył pewne opcje w kernelu. Warto również zauważyć, że wszystkie wydania RedHat'a do i łącznie z wersją 6.2 dostarczane są bez większością opcji odpowiedzialnych za kontrolę ruchu. RedHat 7.2 ma domyślnie włączone wszystko. Upewnij się również, że w konfiguracji jądra zaznaczyłeś obsługę netlink, jeśli będziesz musiał przekompilować swój kernel. Jest on wymagany przez iproute2. _________________________________________________________________ Badanie obecnej konfiguracji Może to być pewnym zaskoczeniem, ale iproute2 jest już skonfigurowane! Obecne komendy ifconfig i route używają zaawansowanych wywołań systemowych, ale w większości z domyślnymi i bardzo bezpiecznymi (tzn. nudnymi) ustawieniami. Głównym narzędziem jest program ip, poprosimy go o wyświetlenie interfejsów. _________________________________________________________________ ip pokazuje nam połączenia [ahu@home ahu]$ ip link list 1: lo: mtu 3924 qdisc noqueue link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 2: dummy: mtu 1500 qdisc noop link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff 3: eth0: mtu 1400 qdisc pfifo_fast qlen 100 link/ether 48:54:e8:2a:47:16 brd ff:ff:ff:ff:ff:ff 4: eth1: mtu 1500 qdisc pfifo_fast qlen 100 link/ether 00:e0:4c:39:24:78 brd ff:ff:ff:ff:ff:ff 3764: ppp0: mtu 1492 qdisc pfifo_fast qlen 10 link/ppp Twój ekran wynikowy może się trochę różnić, ale powyższy wydruk pochodzi z mojego domowego routera NAT. Wytłumaczę tylko część, jako że nie wszystko jest bezpośrednio związane. Na początek widzimy interfejs loopback. O ile twój komputer może i powinien pracować bez niego, radziłbym tego nie próbować. Rozmiar MTU (ang. Maximum Transfer Unit - Maksymalnej Jednostki Transmisji) wynosi 3924 oktety i nie spodziewamy się go kolejkować. Ma to sens, ponieważ interfejs ten jest tylko fantazją twojego kernela. Pominę interfejs dummy, może on nie być obecny na twoim komputerze. Następnie mamy dwa fizyczne interfejsy, jeden po stronie modemu kablowego i drugi podłączony do mojego domowego segmentu ethernetowego. Dalej widzimy interfejs ppp0. Zauważ brak adresów IP. iproute nie używa koncepcji łączenia 'połączeń' z 'adresami IP'. Po wprowadzeniem aliasów IP, koncepcja 'tego konkretnego' adresu IP stała się jakby mniej ważna. Pokazano natomiast adresy MAC, identyfikatory sprzętowe naszych interfejsów ethernetowych. _________________________________________________________________ ip pokazuje nam nasze adresy IP [ahu@home ahu]$ ip address show 1: lo: mtu 3924 qdisc noqueue link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 brd 127.255.255.255 scope host lo 2: dummy: mtu 1500 qdisc noop link/ether 00:00:00:00:00:00 brd ff:ff:ff:ff:ff:ff 3: eth0: mtu 1400 qdisc pfifo_fast qlen 100 link/ether 48:54:e8:2a:47:16 brd ff:ff:ff:ff:ff:ff inet 10.0.0.1/8 brd 10.255.255.255 scope global eth0 4: eth1: mtu 1500 qdisc pfifo_fast qlen 100 link/ether 00:e0:4c:39:24:78 brd ff:ff:ff:ff:ff:ff 3764: ppp0: mtu 1492 qdisc pfifo_fast qlen 10 link/ppp inet 212.64.94.251 peer 212.64.94.1/32 scope global ppp0 Tutaj mamy więcej informacji. Pokazano wszystkie nasze adresy, łącznie ze wskazaniem do których interfejsów należą. 'inet' oznacza Internet (IPv4). Istnieje wiele innych rodzin adresów, ale w tym momencie zupełnie nas nie obchodzą. Przyjrzyjmy się bliżej eth0. Twierdzi, że związany jest z internetowym adresem '10.0.0.1/8'. Co to oznacza? Człon /8 określa ilość bitów, które należą do Adresu Sieci. Są 32 bity, mamy więc 24 bity na określenie części swojej sieci. Pierwsze 8 bitów z 10.0.0.1 odpowiada 10.0.0.0, naszemu Adresowi Sieciowemu, naszą maską jest więc 255.0.0.0. Pozostałe bity należą do przestrzeni adresowej tego interfejsu, więc 10.250.3.13 jest bezpośrednio osiągalny przez eth0, tak samo jak na przykład 10.0.0.1. Z ppp0 jest dokładnie tak samo, mimo że różnią się numerki. Jego adres to 212.64.94.251 bez maski podsieci. Oznacza to, że jest to połączenie punkt-punkt i każdy adres, z wyjątkiem 212.64.94.251 jest zdalny. Jest jednak trochę więcej informacji. Wiemy, że po drugiej stronie połączenia też jest jeden adres - 212.64.94.1. Dodatek /32 oznacza po prostu, że nie ma 'bitów sieci'. Bardzo ważne żebyś zrozumiał opisywane wyżej zagadnienia. Zajrzyj do wspomnianej wcześniej dokumentacji, jeśli masz problemy. Możesz również zauważyć 'qdisc', co oznacza Dyscyplinę Kolejkowania (ang. Queueing Discipline). Znaczenie tej funkcji stanie się jasne później. _________________________________________________________________ ip pokazuje nam trasy routingu Cóż, wiemy jak znaleźć adresy 10.x.y.z i jesteśmy w stanie dotrzeć do 212.64.94.1. To jednak nie wystarcza, musimy mieć jeszcze instrukcje jak dotrzeć do pozostałej części świata. Internet dostępny jest przez nasze połączenie PPP i wygląda na to, że to 212.64.94.1 będzie rozsyłał nasze pakiety po świecie, oraz zbierał i dostarczał odpowiedzi. [ahu@home ahu]$ ip route show 212.64.94.1 dev ppp0 proto kernel scope link src 212.64.94.251 10.0.0.0/8 dev eth0 proto kernel scope link src 10.0.0.1 127.0.0.0/8 dev lo scope link default via 212.64.94.1 dev ppp0 Wydruk jest samoopisujący się. Pierwsze 4 linie wprost informują o tym, co już zostało wydrukowane po użyciu komendy ip address show, ostatnia linia określa, że reszta adresów będzie osiągalna przez adres 212.64.94.1 - domyślną bramkę. Wiemy, że to bramka po słowie via, które oznacza, że wysyłamy pod ten adres pakiety a on bierze na siebie resztę. Dla porównania, poniżej wydruk ze standardowej komendy route: [ahu@home ahu]$ route -n Kernel IP routing table Destination Gateway Genmask Flags Metric Ref Use Iface 212.64.94.1 0.0.0.0 255.255.255.255 UH 0 0 0 ppp0 10.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 eth0 127.0.0.0 0.0.0.0 255.0.0.0 U 0 0 0 lo 0.0.0.0 212.64.94.1 0.0.0.0 UG 0 0 0 ppp0 _________________________________________________________________ ARP ARP, to Protokół Rozwiązywania Adresów (ang. Address Resolution Protocol), opisany w [162]RFC 826. ARP używany jest w komputerach podłączonych do sieci do rozwiązywania adresów sieciowych/lokalizacji innych komputerów również podłączonych do tej sieci. Komputery w Internecie identyfikuje się generalnie po ich nazwach, które rozwiązywane są na adresy IP. W ten właśnie sposób komputer w sieci foo.com może komunikować się z inną maszyną z sieci bar.net. Adresy IP nie mówią jednak nic o fizycznej lokalizacji komputera. Wtedy właśnie pojawia się ARP. Spójrzmy na bardzo prosty przykład. Załóżmy, że mam sieć złożoną z kilku komputerów. Dwa z komputerów w tej sieci, to foo z adresem 10.0.0.1 i bar> z adresem 10.0.0.2. foo chce wykonać ping do bar, by sprawdzić czy bar pracuje, ale nie wie przecież gdzie znajduje się bar. Chcąc wykonać ping, musi zatem najpierw wysłać zapytanie ARP. Zapytanie to podobne jest do wykrzyczenia 'bar (10.0.0.2)! Gdzie jesteś?'. W rezultacie wszystkie komputery w sieci usłyszą zapytanie, ale tylko bar (10.0.0.2) odpowie. Odpowiedź ARP zostanie skierowana bezpośrednio do foo i będzie czymś w rodzaju 'foo (10.0.0.1), jestem pod 00:60:94:E9:08:12'. Po tej prostej wymianie, której użyto by znaleźć kolegę w sieci, foo będzie w stanie komunikować się z bar dopóki zapomni (lub pamięć podręczna arp wygaśnie) gdzie jest bar> (na maszynach Uniksowych standardowo po 15 minutach). A teraz spójrzmy jak to działa. Możesz obejrzeć tabelę sąsiedztwa arp w ten sposób: [root@espa041 /home/src/iputils]# ip neigh show 9.3.76.42 dev eth0 lladdr 00:60:08:3f:e9:f9 nud reachable 9.3.76.1 dev eth0 lladdr 00:06:29:21:73:c8 nud reachable Jak widać, mój komputer espa041 (9.3.76.41) wie gdzie znaleźć espa042 (9.3.76.42) i espagate (9.3.76.1). Spróbujmy teraz dodać inny komputer do pamięci podręcznej arp. [root@espa041 /home/paulsch/.gnome-desktop]# ping -c 1 espa043 PING espa043.austin.ibm.com (9.3.76.43) from 9.3.76.41 : 56(84) bytes of data. 64 bytes from 9.3.76.43: icmp_seq=0 ttl=255 time=0.9 ms --- espa043.austin.ibm.com ping statistics --- 1 packets transmitted, 1 packets received, 0% packet loss round-trip min/avg/max = 0.9/0.9/0.9 ms [root@espa041 /home/src/iputils]# ip neigh show 9.3.76.43 dev eth0 lladdr 00:06:29:21:80:20 nud reachable 9.3.76.42 dev eth0 lladdr 00:60:08:3f:e9:f9 nud reachable 9.3.76.1 dev eth0 lladdr 00:06:29:21:73:c8 nud reachable W wyniku działań espa041, próbującego znaleźć espa043, adres tego ostatniego został dodany do pamięci podręcznej arp. Dopóki ten wpis nie wygaśnie (w rezultacie braku komunikacji pomiędzy obydwoma komputerami), espa041 wie gdzie znaleźć espa043 i nie ma potrzeby rozsyłania zapytań ARP. Teraz wykasujmy espa043 z pamięci podręcznej arp: [root@espa041 /home/src/iputils]# ip neigh delete 9.3.76.43 dev eth0 [root@espa041 /home/src/iputils]# ip neigh show 9.3.76.43 dev eth0 nud failed 9.3.76.42 dev eth0 lladdr 00:60:08:3f:e9:f9 nud reachable 9.3.76.1 dev eth0 lladdr 00:06:29:21:73:c8 nud stale espa041 zapomniał gdzie znaleźć espa043 i będzie musiał wysłać kolejne zapytanie ARP jeśli będzie potrzebował skontaktować się z espa043. Możesz również zauważyć, że stan wpisu przy espagate (9.3.76.1) został zmieniony na 'stale' (niepewny, stary). Oznacza to tylko tyle, że pokazywana lokalizacja jest prawdziwa, ale będzie musiała być potwierdzona przed nawiązaniem jakiegokolwiek połączenia do tego komputera. _________________________________________________________________ Rozdział 4. Reguły - baza danych polityki routingu Jeśli posiadasz duży ruter, możesz zaspokajać potrzeby różnych ludzi, którzy chcą być obsługiwani w różny sposób. Pozwala na to właśnie baza danych polityki routingu, dostarczając możliwości utrzymywania wielu zestawów tabel routingu. Jeśli chcesz użyć tej opcji, upewnij się że twój kernel skompilowano z użyciem opcji "IP: advanced router" oraz "IP: policy routing". Kiedy kernel musi podjąć decyzję o routingu, sprawdza z której tabeli ma skorzystać. Domyślnie, są trzy takie tabele. Stare narzędzie 'route' modyfikuje główną i lokalną, dokładnie tak, jak domyślnie zachowuje się nowe narzędzie ip. Domyślne reguły: [ahu@home ahu]$ ip rule list 0: from all lookup local 32766: from all lookup main 32767: from all lookup default Polecenie listuje priorytety wszystkich reguł. Widać, że wszystkie wpisy dotyczą wszystkich pakietów ( 'from all' ). Widzieliśmy już 'główną' (ang. 'main') tabelę wcześniej, można ją wylistować przez użycie polecenia ip route ls, ale 'lokalna' (ang. 'local') i 'domyślna' (ang. 'default') są nowe. Jeśli chcemy robić wymyślne rzeczy, musimy wygenerować reguły, które wskazywać będą do różnych tabel routingu - innych niż podstawowe, wspólne dla całego systemu. Po dokładny opis tego co kernel robi gdy jest więcej pasujących reguł, odsyłam do dokumentacji ip-cref Aleksieja. _________________________________________________________________ Prosty routing na podstawie źródła (ang. source routing) Wróćmy do prawdziwego przykładu. Mam 2 (dokładniej 3, w momencie gdy je zwracałem) modemy kablowe połączone do linuksowego routera NAT (wykonującego translację adresów sieciowych). Ludzie którzy wokół żyją, płacą mi za używanie Internetu. Powiedzmy, że jeden z nich odwiedza tylko hotmail i chce płacić mniej. Jeśli chodzi o mnie jest to w porządku, ale będą używać starszego modemu. 'Szybki' modem kablowy znany jest jako 212.64.94.251 i jest łączem PPP do 212.64.94.1. 'Wolny' modem ma różne adresy - z uwagi na ten przykład skupimy się na 212.64.78.148 i jest podłączony do 195.96.98.253. Tabela lokalna: [ahu@home ahu]$ ip route list table local broadcast 127.255.255.255 dev lo proto kernel scope link src 127.0.0.1 local 10.0.0.1 dev eth0 proto kernel scope host src 10.0.0.1 broadcast 10.0.0.0 dev eth0 proto kernel scope link src 10.0.0.1 local 212.64.94.251 dev ppp0 proto kernel scope host src 212.64.94.251 broadcast 10.255.255.255 dev eth0 proto kernel scope link src 10.0.0.1 broadcast 127.0.0.0 dev lo proto kernel scope link src 127.0.0.1 local 212.64.78.148 dev ppp2 proto kernel scope host src 212.64.78.148 local 127.0.0.1 dev lo proto kernel scope host src 127.0.0.1 local 127.0.0.0/8 dev lo proto kernel scope host src 127.0.0.1 Widać wiele oczywistych rzeczy, ale gdzieś należy je podać. Cóż, znalazły się tutaj. Tabela domyślna jest pusta. Zajrzyjmy do tabeli głównej: [ahu@home ahu]$ ip route list table main 195.96.98.253 dev ppp2 proto kernel scope link src 212.64.78.148 212.64.94.1 dev ppp0 proto kernel scope link src 212.64.94.251 10.0.0.0/8 dev eth0 proto kernel scope link src 10.0.0.1 127.0.0.0/8 dev lo scope link default via 212.64.94.1 dev ppp0 Generujemy teraz nową regułę, którą nazwiemy `John', tak jak nasz hipotetyczny kolega. Możemy oczywiście operować czystymi numerkami, ale łatwiej jest dodać nasze tabele do pliku /etc/iproute2/rt_tables. # echo 200 John >> /etc/iproute2/rt_tables # ip rule add from 10.0.0.10 table John # ip rule ls 0: from all lookup local 32765: from 10.0.0.10 lookup John 32766: from all lookup main 32767: from all lookup default Teraz pozostało tylko wygenerować tabelę Johna i wyczyścić pamięć podręczną trasowania: # ip route add default via 195.96.98.253 dev ppp2 table John # ip route flush cache I to wszystko. Pozostawiamy jako ćwiczenie dla czytelnika, zaimplementowanie tego w skrypcie ip-up. _________________________________________________________________ Routing dla wielu łącz/do wielu dostawców usług Często spotyka się konfiugrację taką jak poniższa - w której mamy dwóch dostawców Internetu dla sieci lokalnej (lub czasami nawet pojedyńczej maszyny). ________ +------------+ / | | | +-------------+ Dostawca 1 +------- __ | | | / ___/ \_ +------+-------+ +------------+ | _/ \__ | if1 | / / \ | | | | Sieć lokalna -----+ Router | | Internet \_ __/ | Linuksowy | | \__ __/ | if2 | \ \__/ +------+-------+ +------------+ | | | | \ +-------------+ Dostawca 2 +------- | | | +------------+ \________ Zwykle w takiej konfiguracji pojawiają się dwa pytania. _________________________________________________________________ Dostęp rozłączny (ang. split access) Pierwsze to jak routować odpowiedzi na pakiety przychodzące od powiedzmy dostawcy pierwszego, z powrotem tą samą drogą (nie wykorzystując drugiego łącza). Zdefiniujmy parę nazw symbolicznych. Niech $IF1 oznacza pierwszy interfejs (if1 na rysunku powyżej) a $IF2 drugi interfejs. Następnie niech $IP1 będzie adresem IP interfejsu $IF1 a $IP2 adresem IP skojarzonym z interfejsem $IF2. Następnie, niech $P1 oznacza adres IP pierwszego dostawcy a $P2 drugiego. Na koniec niech $P1_NET będzie siecią IP w której znajduje się $P1, a $P2_NET odpowiednio siecią IP w której jest $P2. Należy stworzyć dwa dodatkowe wpisy w tabeli routingu, powiedzmy T1 i T2. Dodaje się je do /etc/iproute2/rt_tables. Następnie należy dodać odpowiednio routing w sposób jak poniżej: ip route add $P1_NET dev $IF1 src $IP1 table T1 ip route add default via $P1 table T1 ip route add $P2_NET dev $IF2 src $IP2 table T2 ip route add default via $P2 table T2 Nic spektakularnego, tworzymy trasy do bramy i dodajemy trasę domyślną przez tą bramę - tak jakbyśmy mieli do czynienia z pojedyńczym dostawcą, z tym wyjątkiem, że mamy osobne tablice dla każdego dostawcy. Zauważ, że wystarczy samo wpisanie trasy - ponieważ mówi jak znaleźć dowolny host w danej sieci, obejmując również bramę tak jak w przykładzie powyżej. Teraz należy skonfigurować główną tabelę routingu. Dobrym pomysłem jest kierować ruch do bezpośredniego sąsiada przez interfejs, którym ten sąsiad jest podłączony. Zwróć uwagę na argumenty 'src' - zapewniają że wybrany zostanie poprawny wychodzący adres IP. ip route add $P1_NET dev $IF1 src $IP1 ip route add $P2_NET dev $IF2 src $IP2 Następnie należy dodać trasę domyślną: ip route add default via $P1 A na koniec skonfigurować tabele routingu. To one wybierają która tablica zostanie wybrana. Chcemy się upewnić, że wykonujemy routing interfejsem, który ma odpowiedni źródłowy adres IP: ip rule add from $IP1 table T1 ip rule add from $IP2 table T2 Powyższy zestaw poleceń powoduje, że wszystkie odpowiedzi na ruch przechodzący przez dany interfejs, zostaną wysłane tym właśnie interfejsem. Ostrzeżenie Czytelnik Rod Roark zauważył: Jeśli $P0_NET to lokalna sieć a $IF0 to jej interfejs, warto dodać dodatkowe wpisy: ip route add $P0_NET dev $IF0 table T1 ip route add $P2_NET dev $IF2 table T1 ip route add 127.0.0.0/8 dev lo table T1 ip route add $P0_NET dev $IF0 table T2 ip route add $P1_NET dev $IF1 table T2 ip route add 127.0.0.0/8 dev lo table T2 ' Jest to oczywiście bardzo prosta konfiguracja. Będzie działała prawidłowo dla wszystkich procesów działających na routerze i dla wszystkich połączeń z sieci lokalnej (jeśli włączono translację adresów). Jeśli translacji nie włączono, to albo dostałeś osobne publiczne przestrzenie adresowe od obu dostawców, albo będziesz chciał robić translację adresów dla jednego z dostawców. W obu przypadkach musisz dodać reguły wybierające przez którego dostawcę routować ruch na podstawie adresów IP maszyn w sieci lokalnej (inicjujących ten ruch). _________________________________________________________________ Rozkładanie obciążenia (ang. load balancing) Drugie pytanie brzmi: jak rozłożyć obciążenie wychodzące na obu dostawców. Odpowiedzią jest: nie jest to specjalnie trudne, jeśli i tak masz już sytuację opisaną w punkcie powyżej. Zamiast wybierać jednego z dostawców jako trasę domyślną, musisz wskazać obu. Domyślnie jądro będzie rozkładało ruch wychodzący pomiędzy dwie trasy domyślne. Konfiguruje się to w ten sposób: ip route add default scope global nexthop via $P1 dev $IF1 weight 1 \ nexthop via $P2 dev $IF2 weight 1 Zapewni to opisaną funkcjonalność. Argumentem weight można dokładnie dostosować metrykę danego dostawcy, efektywnie zwiększając lub zmniejszając ilość ruchu przesyłanego przez niego na rzecz drugiego dostawcy. Zwróć jednak uwagę, że równoważenie obciążenia nie będzie doskonałe, a co więcej bazuje na trasach - które przechowywane są w pamięci podręcznej. Innymi słowy, trasy do najczęściej odwiedzanych stron będą prowadziły zawsze przez tego samego dostawcę. I jeszcze jedno - jeśli naprawdę chcesz coś takiego zastosować, prawdopodobnie powinieneś rzucić okiem na łatki Juliana Anastazowa, znajdujące się pod [163]adresem jego strony, Powinny one ułatwić ci trochę konfigurację takiego podziału ruchu. _________________________________________________________________ Rozdział 5. Tunele - GRE i inne W Linuksie dostępne są trzy rodzaje tuneli. Mamy tunelowanie IP w IP, tunelowanie GRE i tunele żyjące poza jądrem (na przykład PPTP). _________________________________________________________________ Parę ogólnych uwag o tunelach: Tunele można użyć do bardzo niezwykłych i fajnych rzeczy. Mogą również spowodować koszmarne kłopoty jeśli nie skonfiguruje się ich poprawnie. Nie kieruj swojej domyślnej trasy na urządzenie tunelujące chyba, że dokładnie wiesz co robisz :-). Co więcej, tunelowanie zwiększa narzut na pakiety, ponieważ wymaga dodatkowego zestawu nagłówków IP. Oznacza to typowo około 20 bajtów na każdy pakiet, więc jeśli normalny rozmiar pakietu (MTU) w sieci to 1500 bajtów, pakiet wysyłany przez tunel może mieć tylko 1480 bajtów. Nie musi to okazać się od razu wielkim problemem, ale pomyśl o fragmentacji i składaniu pakietów jeśli chcesz łączyć duże sieci tunelami. I jeszcze jedno - najszybszym sposobem wykopania tunelu jest kopanie po obu stronach. _________________________________________________________________ Tunelowanie IP w IP Ten rodzaj tunelowania jest dostępny w Linuksie już od bardzo dawna. Wymaga dwóch modułów kernela: ipip.o oraz new_tunnel.o. Powiedzmy że masz 3 sieci: Wewnętrzne A i B i pośrednią C (na przykład Internet). Mamy więc sieć A: network 10.0.1.0 netmask 255.255.255.0 router 10.0.1.1 W sieci C router ma adres 172.16.17.18. Oraz sieć B: network 10.0.2.0 netmask 255.255.255.0 router 10.0.2.1 W sieci C router ma adres 172.19.20.21. Jeśli chodzi o sieć C, zakładamy że przekazuje pakiety od A do B i odwrotnie. Można do tego używać nawet Internetu. Oto co trzeba zrobić: Po pierwsze, upewnij się że zainstalowano moduły: insmod ipip.o insmod new_tunnel.o Teraz, na routerze sieci A wykonaj: ifconfig tunl0 10.0.1.1 pointopoint 172.19.20.21 route add -net 10.0.2.0 netmask 255.255.255.0 dev tunl0 A na routerze sieci B: ifconfig tunl0 10.0.2.1 pointopoint 172.16.17.18 route add -net 10.0.1.0 netmask 255.255.255.0 dev tunl0 A gdy skończyłeś używać tunelu, wykonaj: ifconfig tunl0 down Presto, koniec. Nie możesz jednak przekazywać pakietów rozgłoszeniowych (ang. broadcasts) i ruchu IPv6 przez tunel IP-w-IP. Można tylko łączyć dwie sieci IPv4, które normalnie nie mogłyby ze sobą rozmawiać - to wszystko. Jeśli chodzi o zgodność w dół, kod znalazł się w jądrze w okolicach wersji 1.3 - jest już obecny zatem jakiś czas. Natomiast jeśli chodzi o inne systemy - ten rodzaj tunelowania nie działa z innymi systemami czy routerami - przynajmniej na tyle na ile wiem. Jest bardzo prosty, ale działa. Używaj jeśli musisz, w przeciwnym razie sięgnij po tunele GRE. _________________________________________________________________ Tunelowanie GRE Protokół tunelujący GRE został pierwotnie stworzony przez Cisco i jest w stanie zrealizować trochę więcej niż tunelowanie IP-w-IP. Na przykład, możesz również transportować ruch multicastowy i IPv6 przez tunel GRE. W Linuksie, będziesz potrzebował modułu ip_gre.o. _________________________________________________________________ Tunelowanie IPv4 Najpierw zajmijmy się tunelowaniem IPv4. Powiedzmy że masz 3 sieci: Wewnętrzne A i B oraz pośrednią C (lub, powiedzmy Internet). Mamy więc sieć A: network 10.0.1.0 netmask 255.255.255.0 router 10.0.1.1 Router ma adres 172.16.17.18 w sieci C. Nazwijmy ją neta (dobra, strasznie oryginalne). ...sieć B: network 10.0.2.0 netmask 255.255.255.0 router 10.0.2.1 Router ma adres 172.19.20.21 w sieci C. Nazwijmy ją netb (nadal mało oryginalne). Natomiast jeśli chodzi o sieć C, zakładamy że przekazuje pakiety od A do B i odwrotnie. Jak i dlaczego nas nie interesuje. Na routerze w sieci A wykonamy: ip tunnel add netb mode gre remote 172.19.20.21 local 172.16.17.18 ttl 255 ip link set netb up ip addr add 10.0.1.1 dev netb ip route add 10.0.2.0/24 dev netb Omówmy to w paru zdaniach. W linii 1 dodaliśmy urządzenie tunelujące i nazwaliśmy je netb (co jest raczej oczywiste, ponieważ to tam ma prowadzić). Co więcej, określiliśmy, że używamy protokołu GRE (mode gre), zdalny adres to 172.19.20.21 (router po drugiej stronie), nasze tunelowane pakiety powinny pochodzić z 172.16.17.18 (dzięki temu możesz mieć wiele interfejsów w sieci C i określić wprost z którego mają być wysyłane pakiety), oraz, że pole TTL pakietów powinno być ustawiane na 255 (ttl 255). Druga linia uaktywnia urządzenie. W trzeciej linii ustaliliśmy adres nowego urządzenia netb na 10.0.1.1. Dla małych sieci jest to w zasadzie w porządku, ale jeśli rozpoczynasz poważne kopanie (WIELE tuneli), powinieneś rozważyć użycie innego zakresu adresów IP dla interfejsów tunelujących (w naszym przykładzie, mógłbyś użyć 10.0.3.0). W czwartej linii ustawiamy trasę dla sieci B. Zauważ inną notację w masce sieciowej. Jeśli nie jest ci ona znana, oto jak działa: zapisujesz maskę sieciową w formie binarnej i liczysz wszystkie jedynki. Jeśli nie wiesz jak to zrobić, zapamiętaj że 255.0.0.0 to /8, 255.255.0.0 to /16 a 255.255.255.0 to /24. A 255.255.254.0 to /23, gdybyś się zastanawiał. Ale koniec tego, zacznijmy z routerem w sieci B. ip tunnel add neta mode gre remote 172.16.17.18 local 172.19.20.21 ttl 255 ip link set neta up ip addr add 10.0.2.1 dev neta ip route add 10.0.1.0/24 dev neta A jeśli zechciałbyś usunąć tunel na routerze A: ip link set netb down ip tunnel del netb Oczywiście, możesz zastąpić netb przez neta dla routera B. _________________________________________________________________ Tunelowanie IPv6 Zajrzyj do sekcji 6, by zapoznać się z krótkim wprowadzeniem do protokołu IPv6 i formacie zapisu adresów. Do roboty z tunelami. Zakładamy, że masz następującą sieć IPv6 i chcesz podłączyć się do 6bone lub kolegi. Network 3ffe:406:5:1:5:a:2:1/96 Twój adres IPv4 to 172.16.17.18 a router IPv4 6bone ma adres 172.22.23.24. ip tunnel add sixbone mode sit remote 172.22.23.24 local 172.16.17.18 ttl 255 ip link set sixbone up ip addr add 3ffe:406:5:1:5:a:2:1/96 dev sixbone ip route add 3ffe::/15 dev sixbone Omówmy to. W pierwszej linii stworzyliśmy tunel nazwany sixbone. Nadaliśmy mu tryb pracy sit (co oznacza tunelowanie IPv6 w IPv4) oraz wskazaliśmy gdzie ma być skierowany (remote) i gdzie się zaczynać (local). TTL ustawione jest na maksymalną wartość 255. Następnie, podnieśliśmy interfejs (up). Na koniec dodaliśmy nasz własny adres sieciowy i ustawiliśmy trasę na 3ffe::/15 (co jest obecnie adresem całej 6bone) w tunelu. Tunele GRE są obecnie preferowanym rodzajem tunelowania. Są standardowe i przyjęte również poza społecznością linuksową, a w związku z tym są Dobrą Rzeczą. _________________________________________________________________ Tunele w przestrzeni użytkownika Istnieją dziesiątki implementacji tunelowania poza kernelem. Najbardziej znane są oczywiście PPP i PPTP, ale jest ich dużo więcej (niektóre firmowe, niektóre bezpieczne, inne nie używają nawet IP) i są zdecydowanie poza tematem tego HOWTO. _________________________________________________________________ Rozdział 6. Tunelowanie IPv6 z Cisco i/lub 6bone Autorstwa Marco Davids UWAGA do koordynatora: Na tyle ile się orientuje tunelowanie IPv6-IPv4 nie jest tak naprawdę tunelowaniem GRE. Można tunelować IPv6 przez IPv4 w urządzeniach które tworzą tunel GRE (GRE tuneluje WSZYSTKO w IPv4), ale urządzenie używane w tym rozwiązaniu tuneluje tylko IPv6 przez IPv4 i w związku z tym to co innego. _________________________________________________________________ Tunelowanie IPv6 To kolejne zastosowanie dla możliwości tunelowania Linuksa. Jest bardzo popularne u pierwszych użytkowników IPv6, lub pionierów - jak wolicie. Ten praktyczny przykład z pewnością nie opisuje jedynego możliwego rozwiązania tunelowania IPv6. Jest to jednak metoda używana często przy tunelowaniu Linuksów i Cisco używających IPv6, a z doświadczenia wynika, że w ten właśnie sposób realizuje to większość ludzi. Dziesięć do jednego, że dotyczy to również ciebie ;-). Trochę o adresach IPv6: Adresy IPv6 są, w porównaniu do IPv4 bardzo duże: 128 bitów zamiast 32. Daje nam to czego potrzebujemy: bardzo, bardzo dużo adresów IP: 340,282,266,920,938,463,463,374,607,431,768,211,465 jeśli chodzi o ścisłość. Oprócz tego, IPv6 (lub IPng, co oznacza IP Next Generation) ma zapewnić mniejsze tablice routingu na ruterach szkieletowych, prostszą konfigurację sprzętu, lepsze bezpieczeństwo na poziomie IP i lepsze wsparcie dla QoS. Przykład: 2002:836b:9820:0000:0000:0000:836b:9886 Zapisanie adresu IPv6 może być dosyć kłopotliwe. By życie było prostsze, stworzono pewne reguły: * Nie używaj wiodących zer. Tak jak w IPv4. * Używaj dwukropków do oddzielania każdych 16 bitów, lub dwóch bajtów. * Jeśli masz dużo kolejnych zer, możesz zapisać je jako :: - ale tylko raz i tylko dla wielokrotności 16 bitów. Adres 2002:836b:9820:0000:0000:0000:836b:9886 może być więc zapisany jako 2002:836b:9820::836b:9886, co jest zdecydowanie przyjaźniejsze. Inny przykład: 3ffe:0000:0000:0000:0000:0020:34A1:F32C może być zapisane jako 3ffe::20:34A1:F32C, co jest dużo krótsze. IPv6 ma zastąpić obecnie używany IPv4. Ponieważ jest to relatywnie nowa technologia, nie ma jeszcze światowej natywnej sieci IPv6. By można było przenosić się płynnie, wprowadzono 6bone. Sieci używające IPv6 połączone są pomiędzy sobą przez hermetyzację protokołu IPv6 w ramki IPv4 i wysyłanie tak spreparowanych pakietów przez istniejącą infrastrukturę IPv4 z jednej sieci IPv6 do innej. Miejsce styku obu sieci jest dokładnie miejscem, w którym używamy tuneli. By móc używać IPv6 musimy mieć kernel, który obsługuje ten protokół. Jest wiele bardzo dobrych dokumentów jak dojść do takiego stanu. Wszystko sprowadza się jednak do paru kroków: * Zdobądź którąś z nowszych dystrybucji Linuksa z odpowiednią wersją biblioteki glibc. * Zdobądź nowe źródła kernela. Jeśli wszystko jest gotowe, to możesz skompilować nowy kernel z obsługą IPv6: * Przejdź do /usr/src/linux i napisz: * make menuconfig * Wybierz "Networking Options" * Wybierz "The IPv6 protocol", "IPv6: enable EUI-64 token format" i "IPv6: disable provider based addresses" PODPOWIEDŹ: Nie konfiguruj tych opcji jako znajdujących się w modułach. Bardzo często takie rozwiązanie po prostu nie działa. Innymi słowy, wkompiluj IPv6 w kernel. Możesz następnie zapisać swoją konfigurację tak jak zwykle i skompilować kernel. PODPOWIEDŹ: Zanim zaczniesz to robić, zastanów się nad modyfikacją pliku Makefile: EXTRAVERSION = -x ; --> ; EXTRAVERSION = -x-IPv6 Napisano bardzo dużo dobrej dokumentacji dotyczącej kompilowania i instalowania jądra, ale ten dokument jest o czym innym. Jeśli w tym momencie wpadłeś w jakieś problemy, poszukaj czegoś na ten temat w innych dokumentach. Plik /usr/src/linux/README może być dobrym początkiem. Gdy uda ci się kompilacja i uruchomiłeś ponownie swój komputer z nowym kernelem, możesz sprawdzić przez '/sbin/ifconfig -a' czy pojawiło się nowe urządzenie 'sit0-device'. SIT oznacza Proste Przejście do Internetu (ang. Simple Internet Transition). Możesz sobie pogratulować - jesteś o jeden krok bliżej do IP Nowej Generacji ;-). Teraz drugi krok. Chcesz połączyć twoj komputer lub być może całą sieć LAN do innej sieci IPv6. Może to być właśnie "6bone", którą stworzono właśnie w tym celu. Załóżmy, że masz następującą sieć IPv6: 3ffe:604:6:8::/64 i że chcesz połączyć się do 6bone lub kolegi. Zauważ notację /64, która działa dokładnie tak samo jak w zwykłym adresie IP. Masz adres IPv4 145.100.24.181 a router 6bone ma adres 145.100.1.5. # ip tunnel add sixbone mode sit remote 145.100.1.5 [local 145.100.24.181 ttl 2 55] # ip link set sixbone up # ip addr add 3FFE:604:6:7::2/126 dev sixbone # ip route add 3ffe::0/16 dev sixbone Omówmy to. W pierwszej linii tworzymy tunel nazwany sixbone. Później ustawiamy go w tryb sit, mówimy gdzie ma być skierowany (remote) i gdzie się zaczynać (local). TTL ustawione jest na wartość maksymalną - 255. Następnie podnosimy nasze urządzenie (up). Potem dodajemy adres naszej sieci i ustawiamy trasę dla 3ffe::/15 (które stanowi obecnie całe 6bone) przez tunel. Jeśli ta konkretna maszyna na której wpisujesz te komendy jest twoją bramką do IPv6, rozważ dodanie następujących linii: # echo 1 >/proc/sys/net/ipv6/conf/all/forwarding # /usr/local/sbin/radvd Drugi program, radvd, jest podobnie jak zebra demonem rozgłaszającym routera i zapewnia wsparcie dla opcji autokonfiguracyjnych IPv6. Poszukaj go ulubioną wyszukiwarką, jeśli chciałbyś go używać. Możesz sprawdzić rzeczy takie jak np.: # /sbin/ip -f inet6 addr Jeśli radvd pracuje na bramce IPv6 a twój Linux jest w lokalnym LANie, będziesz mógł cieszyć się autokonfiguracją IPv6: # /sbin/ip -f inet6 addr 1: lo: mtu 3924 qdisc noqueue inet6 ::1/128 scope host 3: eth0: mtu 1500 qdisc pfifo_fast qlen 100 inet6 3ffe:604:6:8:5054:4cff:fe01:e3d6/64 scope global dynamic valid_lft forever preferred_lft 604646sec inet6 fe80::5054:4cff:fe01:e3d6/10 scope link Powinieneś teraz skonfigurować swojego binda na obsługę adresów IPv6. Rekordowi typu A w IPv4 odpowiada w IPv6 AAAA. Natomiast wpisowi in-addr.arpa odpowiada teraz ip6.int. Jest bardzo dużo informacji dostępnej na ten temat. Dostępne są stale rosnące ilości aplikacji obsługujących IPv6, łącznie z bezpieczną powłoką (ssh), telnetem, inetd, przeglądarką Mozilla, serwerem WWW Apache i innymi. Ale ponownie - zakres aplikacji wspierających IPv6 wykracza daleko poza ten dokument ;-). W konfiguracji routera Cisco należy wpisać coś takiego: ! interface Tunnel1 description tunel IPv6 no ip address no ip directed-broadcast ipv6 address 3FFE:604:6:7::1/126 tunnel source Serial0 tunnel destination 145.100.24.181 tunnel mode ipv6ip ! ipv6 route 3FFE:604:6:8::/64 Tunnel1 Jeśli nie masz do swojej dyspozycji Cisco, popytaj innych użytkowników IPv6 w Internecie. Być może będą chcieli skonfigurować swoje Cisco z twoim tunelem, zwykle w ramach przyjaznego interfejsu WWW. Szukaj ciągu znaków "ipv6 tunnel broker" w ulubionej wyszukiwarce. _________________________________________________________________ Rozdział 7. IPsec: bezpieczne IP przez Internet W Linuksie dostępne są dwie implementacje IPsec. Dla kerneli 2.2 i 2.4 istnieje FreeS/WAN, który był pierwszym kompletnym rozwiązaniem. Oficjalna strona znajduje się [164]pod tym adresem, natomiast nieoficjalna [165]pod tym. FreeS/WAN tradycyjnie nie był włączony do źródeł jądra z paru powodów. Zwykle wymienia się 'polityczne' - związane z Amerykanami pracującymi nad kodem odpowiedzialnym za szyfrowanie. Co więcej, niezbyt dobrze integrował się z jądrem i w związku z tym nie był dobrym kandydatem. Dodatkowo [166]wiele niezależnych [167]osób wyrażało się niezbyt pochlebnie o jakości kodu. Do konfiguracja FreeS/WAN dostępne jest natomiast [168]dużo rozmaitej [169]dokumentacji. Od wersji 2.5.47 dostępna jest natywna implementacja IPsec. Została napisana przez Aleksieja Kuzniecowa i Dave Millera, a jej konstrukcja inspirowana była pracą grupy USAGI IPv6. Jednocześnie w tej wersji dołączono kod Jamesa Morrisa - CryptoAPI. Ten dokument opisuje tylko wersje 2.5+ IPsec. Dla jąder serii 2.4 zalecany jest obecnie FreeS/WAN, ale zwróć uwagę że jego konfiguracja różni się od natywnej implementacji IPsec. Dostępne są obecnie [170]łatki umożliwiające współpracę kodu FreeS/WAN z natywną implementacją IPsec. Od wersji 2.5.49, implementacja IPsec działa bez żadnych dodatkowych łatek. Notatka: Narzędzia działające w przestrzeni użytkownika dostępne są [171]tutaj - i są aktywnie rozwijane. Podczas kompilacji jądra, upewnij się że włączyłeś 'PF_KEY', 'AH', 'ESP' oraz wszystko w CryptoAPI! Natomiast cel 'TCP_MSS' z netfilter aktualnie nie działa i powinieneś go wyłączyć. Ostrzeżenie Autor tego rozdziału jest zupełnym początkującym jeśli chodzi o IPsec. Jeśli znalazłeś błędy, proszę napisz do Berta Huberta <[172]ahu%ds9a.nl>. Na początek, pokażemy jak ręcznie skonfigurować bezpieczny kanał komunikacyjny pomiędzy dwoma hostami. Większość tego procesu można zautomatyzować, ale zrobimy to ręcznie by zapoznać się z tym, co dzieje się 'pod maską'. Możesz pominąć tą sekcję, jeśli interesujesz się tylko automatyczną wymianą kluczy, ale weź pod uwagę że lepiej wiedzieć o tym procesie coś więcej. _________________________________________________________________ Wprowadzenie do Ręcznej Wymiany Kluczy IPsec to skomplikowany zestaw protokołów. Bardzo dużo materiału dostępnego jest online, natomiast to HOWTO skupi się tylko na uruchomieniu podstawowej funkcjonalności. Notatka: Wiele konfiguracji używających iptables odrzuca pakiety IPsec! By je przepuszczać dodaj: 'iptables -A xxx -p 50 -j ACCEPT' and 'iptables -A xxx -p 51 -j ACCEPT' IPsec oferuje bezpieczną wersję IP. Bezpieczeństwo rozumiane jest tutaj jako dwie różne rzeczy: szyfrowanie i uwierzytelnianie. Naiwna wersja bezpieczeństwa oferuje tylko szyfrowanie, ale bardzo łatwo można pokazać, że to nie wystarczy - możesz komunikować się w sposób szyfrowany, ale nie masz gwarancji że to co oferujesz drugiej stronie jest tym, czym spodziewasz się że będzie. IPsec wspiera 'Encapsulated Security Payload' (ESP) który służy do szyfrowania oraz 'Authentication Header' (AH) dla uwierzytelniania zdalnego partnera. Możesz użyć obu z nich, lub zdecydować się na zastosowanie tylko jednego. Zarówno ESP i AH opierają się o tzw. związki bezpieczeństwa (ang. "security association", SA). SA składają się ze wskazania źródła ruchu, miejsca docelowego dla ruchu oraz instrukcji jak przesyłany ruch potraktować. Przykładowa SA uwierzytelniająca pokazana jest poniżej: add 10.0.0.11 10.0.0.216 ah 15700 -A hmac-md5 "1234567890123456"; Wpis ten oznacza 'ruch z 10.0.0.11 do 10.0.0.216 wymagający zastosowania AH, należy podpisać używając algorytmu HMAC-MD5 z kluczem 1234567890123456'. Instrukcja ta posiada przydzielony indeks parametrów bezpieczeństwa (ang. "Security Parameter Index", SPI) 15700. Interesującą cechą SA jest to, że są symetryczne. Obie strony muszą posiadać dokładnie te same SA - nie są one automatycznie kopiowane do partnera. Co więcej, pojedyńczy wpis SA opisuje ruch tylko w jednym kierunku. Aby obsłużyć ruch powrotny, należy stworzyć dwie SA. Przykładowe SA ESP: add 10.0.0.11 10.0.0.216 esp 15701 -E 3des-cbc "1234567890121234567890 12"; Ten z kolei wpis oznacza 'ruch z 10.0.0.11 do 10.0.0.216 wymagający szyfrowania należy szyfrować algorytmem 3DES-CBC z kluczem 123456789012123456789012'. SPI dla wpisu to 15701. Udało nam się stworzyć SA opisujące różne możliwe instrukcje, ale nie wskazaliśmy w żadnym momencie w którym momencie należy tych instrukcji użyć. Tak naprawdę, można stworzyć wiele dokładnie takich samych SA różniących się tylko numerami SPI. Aby wykonać faktycznie szyfrowanie, należy dodatkowo zdefiniować regułę. Może ona na przykład mówić 'użyj IPsec jeśli jest dostępny' lub 'odrzucaj ruch chyba że otrzymamy IPsec'. Typowa, przykładowa Reguła Bezpieczeństwa (ang. "Security Policy", SP) wyglądać może tak: spdadd 10.0.0.216 10.0.0.11 any -P out ipsec esp/transport//require ah/transport//require; Jeśli została wpisana na hoście 10.0.0.216, oznacza dokładnie tyle, że cały ruch wychodzący do 10.0.0.11 ma być szyfrowany i opakowany w nagłówek AH. Zauważ że nie wskazaliśmy które SA mają być zastosowane, ma to sprawdzić kernel. Innymi słowy, SP opisuje CO chcemy uzyskać, a SA JAK chcemy to zrobić. Wychodzące pakiety otrzymują etykiety z SA SPI, tak by host otrzymujący ruch mógł sprawdzić swoje wpisy i wykonać odpowiednie operacje odszyfrowując ruch. Poniżej bardzo prosty przykład konfiguracji, dla połączenia 10.0.0.216 z 10.0.0.11 z użyciem uwierzytelniania i szyfrowania. Weź pod uwagę że ruch z powrotem odbywać się będzie bez szyfrowania i taka konfiguracja nie powinna być stosowana. Na hoście 10.0.0.216: #!/sbin/setkey -f add 10.0.0.216 10.0.0.11 ah 24500 -A hmac-md5 "1234567890123456"; add 10.0.0.216 10.0.0.11 esp 24501 -E 3des-cbc "123456789012123456789012"; spdadd 10.0.0.216 10.0.0.11 any -P out ipsec esp/transport//require ah/transport//require; Na hoście 10.0.0.11 te same SA, bez SP: #!/sbin/setkey -f add 10.0.0.216 10.0.0.11 ah 24500 -A hmac-md5 "1234567890123456"; add 10.0.0.216 10.0.0.11 esp 24501 -E 3des-cbc "123456789012123456789012"; W powyższej konfiguracji (pliki można wykonać jeśli 'setkey' znajduje się w katalogu /sbin) polecenie 'ping 10.0.0.11' wykonane na hoście 10.0.0.216 wyglądać powinno tak, jeśli podsłuchamy je programem tcpdump: 22:37:52 10.0.0.216 > 10.0.0.11: AH(spi=0x00005fb4,seq=0xa): ESP(spi=0x00005fb5 ,seq=0xa) (DF) 22:37:52 10.0.0.11 > 10.0.0.216: icmp: echo reply Zauważ, że odpowiedź na ping z 10.0.0.11 jest faktycznie widoczna nieszyfrowana. Natomiast sam ping widziany jest przez tcpdump jako transmisja IPsec (używająca AH i ESP). Należy zwrócić uwagę na parę rzeczy. Konfiguracja powyżej pokazywana jest w wielu przykładach dotyczących IPsec i jest bardzo niebezpieczna. Problem polega na tym, że 10.0.0.11 nie szyfruje ruchu powrotnego oraz na tym, że 10.0.0.11 nie odrzuca ruchu niezaszyfrowanego lub nieuwierzytelnionego. Dowolny użytkownik sieci może wysłać sfałszowany ruch do 10.0.0.11 a ten go przyjmie. Aby zapobiec temu problemowi, potrzebujemy SP na hoście 10.0.0.11 która wygląda tak: #!/sbin/setkey -f spdadd 10.0.0.216 10.0.0.11 any -P IN ipsec esp/transport//require ah/transport//require; To polecenie powoduje, że cały ruch z 10.0.0.216 musi posiadać prawidłowe ESP i AH. Pierwszy problem (nieszyfrowanie ruchu powrotnego) rozwiążemy uzupełniając konfigurację. Na 10.0.0.216: #!/sbin/setkey -f flush; spdflush; # AH add 10.0.0.11 10.0.0.216 ah 15700 -A hmac-md5 "1234567890123456"; add 10.0.0.216 10.0.0.11 ah 24500 -A hmac-md5 "1234567890123456"; # ESP add 10.0.0.11 10.0.0.216 esp 15701 -E 3des-cbc "123456789012123456789012"; add 10.0.0.216 10.0.0.11 esp 24501 -E 3des-cbc "123456789012123456789012"; spdadd 10.0.0.216 10.0.0.11 any -P out ipsec esp/transport//require ah/transport//require; spdadd 10.0.0.11 10.0.0.216 any -P in ipsec esp/transport//require ah/transport//require; I na 10.0.0.11: #!/sbin/setkey -f flush; spdflush; # AH add 10.0.0.11 10.0.0.216 ah 15700 -A hmac-md5 "1234567890123456"; add 10.0.0.216 10.0.0.11 ah 24500 -A hmac-md5 "1234567890123456"; # ESP add 10.0.0.11 10.0.0.216 esp 15701 -E 3des-cbc "123456789012123456789012"; add 10.0.0.216 10.0.0.11 esp 24501 -E 3des-cbc "123456789012123456789012"; spdadd 10.0.0.11 10.0.0.216 any -P out ipsec esp/transport//require ah/transport//require; spdadd 10.0.0.216 10.0.0.11 any -P in ipsec esp/transport//require ah/transport//require; Zauważ, że użyliśmy w przykładzie identycznych kluczy do obu kierunków ruchu - nie jest to oczywiście wymagane. Aby obejrzeć konfigurację, którą przed chwilą stworzyłeś wykonaj polecenie setkey -D, która pokazuje SA lub polecenie setkey -DP pokazująca skonfigurowane reguły. _________________________________________________________________ Automatyczna wymiana kluczy W poprzedniej sekcji, szyfrowanie odbywało się za pomocą prostych kluczy. Innymi słowy, aby pozostać bezpiecznym, musiałbyś wymieniać konfigurację przez zestawiony uprzednio bezpieczny kanał. Jeśli musielibyśmy skonfigurować zdalnego hosta przez telnet, dowolny użytkownik mógłby podsłuchać klucze i konfiguracja nie byłaby bezpieczna. Co więcej, ponieważ klucz jest współdzielony - nie jest już wcale tajny. Zdalny partner nie jest w stanie zrobić wiele znając go, ale mając wielu partnerów musimy zadbać by z każdym rozmawiać za pomocą różnych kluczy. Wymaga to dużej ilości kluczy - przy 10 partnerach musimy znać przynajmniej 50 różnych kluczy. Poza problemami związanymi z kluczem symetrycznym, mamy jeszcze problem zmiany klucza. Jeśli osoba postronna podsłucha odpowiednio dużo ruchu, może być w stanie odzyskać klucz i odczytać nasze transmisje. Zapobiega się temu zmieniając regularnie klucz, ale należy to oczywiście zautomatyzować. Inny problem związany z manualnym zarządzaniem kluczami jest dokładne ustalenie z partnerem parametrów połączenia - algorytmów szyfrowania i/lub uwierzytelniania. Wygodniej jest móc określić regułę w postaci 'Możemy używać 3DESa i Blowfish z kluczami o przynajmniej takiej a takiej długości'. Aby rozwiązać te problemy, IPsec wprowadził standard wymiany kluczy w Internecie (ang. "Internet Key Exchange", IKE). Umożliwia on automatyczną wymianę losowo generowanych kluczy, wykorzystującą technologię szyfrowania asymetrycznego zgodnie z wynegocjowanymi z partnerem parametrami. Implementacja IPsec Linuksa 2.5 działa z demonem 'racoon' pochodzącym z projektu KAME. Od 9 listopada wersja oparta o racoon znajduje się w dystrybucji iptools Aleksieja, aczkolwiek żeby ją skompilować będziesz musiał usunąć #include w dwóch plikach. Możesz również ściągnąć [173]wersję prekompilowaną. Notatka: IKE wymaga dostępu z Internetu do portu 500/udp, upewnij się że iptables nie blokują go. _________________________________________________________________ Teoria Tak jak wyjaśniałem wcześniej, automatyczna zmiana kluczy może wykonać za nas masę pracy. Dokładniej rzecz biorąc, tworzy SA "w locie". Nie powoduje jednak pojawienia się reguł bezpieczeństwa i w zasadzie tak powinno być. Żeby skorzystać z IKE, skonfiguruj regułę ale nie wskazuj SA. Kernel w momencie odkrycia reguły IPsec bez SA powiadomi demon IKE, który z kolei postara się odpowiednie SA wynegocjować. Powtórzmy to jeszcze raz: SP opisuje CO chcemy uzyskać, a SA JAK chcemy to zrobić. Zastosowanie demona IKE automatycznie wymieniającego klucze umożliwia nam ograniczenie się tylko do wskazania, CO chcemy uzyskać. _________________________________________________________________ Przykład Racoon w wersji KAME dostarczany jest z całą masą opcji, z których większość ma bardzo dobre ustawienia domyślne więc nie będziemy ich modyfikować. Tak jak opisałem powyżej, musimy zdefiniować reguły bezpieczeństwa (SP) a nie SA. Pozostawimy ich negocjację demonowi IKE. W tym przykładzie, 10.0.0.11 i 10.0.0.216 ponownie chcą zestawić kanał komunikacyjny, ale tym razem z pomocą demona racoon. Dla zachowania przejrzystości konfiguracji użyjemy współdzielonych kluczy (ang. "pre-shared keys"). Zastosowanie do uwierzytelnienia partnerów certyfikatów X.509 opisano w osobnym rozdziale, zajrzyj do [174]sekcja Automatyczna wymiana kluczy przy użyciu certyfikatów X.509. Użyjemy prawie domyślnej konfiguracji, identycznej na obu hostach: path pre_shared_key "/usr/local/etc/racoon/psk.txt"; remote anonymous { exchange_mode aggressive,main; doi ipsec_doi; situation identity_only; my_identifier address; lifetime time 2 min; # sec,min,hour initial_contact on; proposal_check obey; # obey, strict or claim proposal { encryption_algorithm 3des; hash_algorithm sha1; authentication_method pre_shared_key; dh_group 2 ; } } sainfo anonymous { pfs_group 1; lifetime time 2 min; encryption_algorithm 3des ; authentication_algorithm hmac_sha1; compression_algorithm deflate ; } Wiele ustawień - myślę że można usunąć więcej by zbliżyć się do domyślnej konfiguracji. Parę rzeczy wartych zwrócenia uwagi - stworzyliśmy dwie sekcje anonimowe pasujące dla wszystkich partnerów zdalnych co ułatwia znakomicie konfigurację. Nie musimy definiować wszystkiego per-host, chyba że bardzo chcemy. Co więcej, konfiguracja została wykonana w ten sposób, że identyfikowani jesteśmy na podstawie adresu IP ('my_identifier address') i deklarujemy, że jesteśmy w stanie używać algorytmów 3DES, SHA-1 oraz, że będziemy używać współdzielonego klucza zlokalizowanego w pliku psk.txt. W rzeczonym pliku psk.txt wpisujemy po jednej linicje, która różnić się będzie na obu hostach. Na 10.0.0.11 będzie to: 10.0.0.216 password2 a na 10.0.0.216: 10.0.0.11 password2 Upewnij się że właścicielem tych plików jest root i uprawnienia do nich ustawiono na 0600 - w innym przypadku racoon nie będzie chciał ich użyć. Jeśli jeszcze tego nie zauważyłeś, zwróć uwagę że te dwa pliki są swoimi lustrzanymi odbiciami. Teraz jesteśmy w stanie skonfigurować żądaną SP, która jest bardzo prosta. Na hoście 10.0.0.216: #!/sbin/setkey -f flush; spdflush; spdadd 10.0.0.216 10.0.0.11 any -P out ipsec esp/transport//require; spdadd 10.0.0.11 10.0.0.216 any -P in ipsec esp/transport//require; I na 10.0.0.11: #!/sbin/setkey -f flush; spdflush; spdadd 10.0.0.11 10.0.0.216 any -P out ipsec esp/transport//require; spdadd 10.0.0.216 10.0.0.11 any -P in ipsec esp/transport//require; Tu również widać, że SP są swoimi lustrzanymi odbiciami. W tym momencie jesteśmy gotowi do uruchomienia racoon! Jeśli już działa, zainicjowanie ruchu z 10.0.0.11 do 10.0.0.216 lub odwrotnie (np. za pomocą telnetu) spowoduje rozpoczęcie przez racoon negocjacji: 12:18:44: INFO: isakmp.c:1689:isakmp_post_acquire(): IPsec-SA request for 10.0.0.11 queued due to no phase1 found. 12:18:44: INFO: isakmp.c:794:isakmp_ph1begin_i(): initiate new phase 1 negotiation: 10.0.0.216[500]<=>10.0.0.11[500] 12:18:44: INFO: isakmp.c:799:isakmp_ph1begin_i(): begin Aggressive mode. 12:18:44: INFO: vendorid.c:128:check_vendorid(): received Vendor ID: KAME/racoon 12:18:44: NOTIFY: oakley.c:2037:oakley_skeyid(): couldn't find the proper pskey, try to get one by the peer's address. 12:18:44: INFO: isakmp.c:2417:log_ph1established(): ISAKMP-SA established 10.0.0.216[500]-10.0.0.11[500] spi:044d25dede78a4d1:ff01e5b4804f0 680 12:18:45: INFO: isakmp.c:938:isakmp_ph2begin_i(): initiate new phase 2 negotiation: 10.0.0.216[0]<=>10.0.0.11[0] 12:18:45: INFO: pfkey.c:1106:pk_recvupdate(): IPsec-SA established: ESP/Transport 10.0.0.11->10.0.0.216 spi=44556347(0x2a7e03b) 12:18:45: INFO: pfkey.c:1318:pk_recvadd(): IPsec-SA established: ESP/Transport 10.0.0.216->10.0.0.11 spi=15863890(0xf21052) Jeśli w tym momencie wykonamy polecenie `setkey -D', które pokazuje nam nasze SA, powinny wyglądać mniej więcej tak: 10.0.0.216 10.0.0.11 esp mode=transport spi=224162611(0x0d5c7333) reqid=0(0x00000000) E: 3des-cbc 5d421c1b d33b2a9f 4e9055e3 857db9fc 211d9c95 ebaead04 A: hmac-sha1 c5537d66 f3c5d869 bd736ae2 08d22133 27f7aa99 seq=0x00000000 replay=4 flags=0x00000000 state=mature created: Nov 11 12:28:45 2002 current: Nov 11 12:29:16 2002 diff: 31(s) hard: 600(s) soft: 480(s) last: Nov 11 12:29:12 2002 hard: 0(s) soft: 0(s) current: 304(bytes) hard: 0(bytes) soft: 0(bytes) allocated: 3 hard: 0 soft: 0 sadb_seq=1 pid=17112 refcnt=0 10.0.0.11 10.0.0.216 esp mode=transport spi=165123736(0x09d79698) reqid=0(0x00000000) E: 3des-cbc d7af8466 acd4f14c 872c5443 ec45a719 d4b3fde1 8d239d6a A: hmac-sha1 41ccc388 4568ac49 19e4e024 628e240c 141ffe2f seq=0x00000000 replay=4 flags=0x00000000 state=mature created: Nov 11 12:28:45 2002 current: Nov 11 12:29:16 2002 diff: 31(s) hard: 600(s) soft: 480(s) last: hard: 0(s) soft: 0(s) current: 231(bytes) hard: 0(bytes) soft: 0(bytes) allocated: 2 hard: 0 soft: 0 sadb_seq=0 pid=17112 refcnt=0 Powinny być również widoczne reguły bezpieczeństwa, które skonfigurowaliśmy sami: 10.0.0.11[any] 10.0.0.216[any] tcp in ipsec esp/transport//require created:Nov 11 12:28:28 2002 lastused:Nov 11 12:29:12 2002 lifetime:0(s) validtime:0(s) spid=3616 seq=5 pid=17134 refcnt=3 10.0.0.216[any] 10.0.0.11[any] tcp out ipsec esp/transport//require created:Nov 11 12:28:28 2002 lastused:Nov 11 12:28:44 2002 lifetime:0(s) validtime:0(s) spid=3609 seq=4 pid=17134 refcnt=3 _________________________________________________________________ Problemy i znane usterki Jeśli coś poszło nie tak, sprawdź czy wszystkie pliki konfiguracyjne należą do roota i tylko przez niego mogą zostać przeczytane. Aby uruchomić demon racoon na pierwszym planie użyj opcji `-F'. By zmusić go do przeczytania innego pliku konfiguracyjnego niż domyślnego, użyj opcji `-f'. Aby obejrzeć dokładne informacje o wszystkim co demon robi, dodaj do pliku racoon.conf linijkę zawierającą `log debug;'. _________________________________________________________________ Automatyczna wymiana kluczy przy użyciu certyfikatów X.509 Wspominałem wcześniej, że zastosowanie współdzielonych kluczy nie jest najwygodniejsze, ponieważ nie są zbyt łatwe do bezpiecznego współdzielenia - a gdy już są współdzielone, przestają być sekretem. Na szczęście w tym momencie technologie wykorzystujące kryptografię asymetryczną wchodzą do gry i rozwiązują problem. Jeśli każdy z partnerów IPsec stworzy klucz prywatny i publiczny, możemy zestawić bezpieczny kanał komunikacyjny pozwalając obu stronom opublikować swoje klucze publiczne i odpowiednio skonfigurować reguły bezpieczeństwa. Stworzenie klucza jest relatywnie łatwe, aczkolwiek wymaga trochę pracy. Poniższy opis opiera się o zastosowanie do tego celu narzędzia openssl. _________________________________________________________________ Tworzenie certyfikatu X.509 dla twojego hosta OpenSSL dysponuje bogatą infrastrukturą związaną z operacjami na kluczach, podpisanych lub niepodpisanych przez autorytety/instytucje certyfikujące (ang. "certificate authorities"). Na razie zajmiemy się domyślnie generowanymi kluczami bez użycia instytucji certyfikującej. Najpierw stworzymy `prośbę o certyfikat' dla naszego hosta, nazwanego `laptop': $ openssl req -new -nodes -newkey rsa:1024 -sha1 -keyform PEM -keyout \ laptop.private -outform PEM -out request.pem Polecenie spowoduje konieczność odpowiedzi na parę pytań: Country Name (2 letter code) [AU]:NL State or Province Name (full name) [Some-State]:. Locality Name (eg, city) []:Delft Organization Name (eg, company) [Internet Widgits Pty Ltd]:Linux Advanced Routing & Traffic Control Organizational Unit Name (eg, section) []:laptop Common Name (eg, YOUR name) []:bert hubert Email Address []:ahu@ds9a.nl Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []: Od ciebie zależy ile z informacji podasz i w jaki sposób je podasz. Może to zależeć od twoich oczekiwań związanych z bezpieczeństwem. W powyższym przykładzie podaliśmy większość informacji. Teraz sami `podpiszemy' swoją prośbę: $ openssl x509 -req -in request.pem -signkey laptop.private -out \ laptop.public Signature ok subject=/C=NL/L=Delft/O=Linux Advanced Routing & Traffic \ Control/OU=laptop/CN=bert hubert/Email=ahu@ds9a.nl Getting Private key Plik request.pem można teraz usunąć. Powtórz tą operację dla wszystkich hostów które potrzebować będą klucza. Możesz oczywiście dystrybuować plik .public, ale plik .private trzymaj z daleka od wszystkich w tajemnicy. _________________________________________________________________ Konfiguracja i uruchomienie Po wygenerowaniu kluczy należy poinformować racoon by ich użył. Wracamy w tym momencie do naszej poprzedniej konfiguracji pomiędzy dwoma hostami - 10.0.0.11 ('upstairs') i 10.0.0.216 ('laptop'). Do pliku racoon.conf na 10.0.0.11 dodaj: path certificate "/usr/local/etc/racoon/certs"; remote 10.0.0.216 { exchange_mode aggressive,main; my_identifier asn1dn; peers_identifier asn1dn; certificate_type x509 "upstairs.public" "upstairs.private"; peers_certfile "laptop.public"; proposal { encryption_algorithm 3des; hash_algorithm sha1; authentication_method rsasig; dh_group 2 ; } } Taka konfiguracja spowoduje, że racoon poszuka kluczy w katalogu /usr/local/etc/racoon/certs/. Konfiguracja zawiera również elementy specyficzne dla zdalnego hosta 10.0.0.216. Linijka asn1dn informuje racoon, że identyfikator obu stron należy pobrać z kluczy publicznych. Będzie to ciąg znaków `subject=/C=NL/L=Delft/O=Linux Advanced Routing & Traffic Control/OU=laptop/CN=bert hubert/Email=ahu@ds9a.nl'. Linijka certificate_type wskazuje lokalny klucz prywatny i publiczny. Natomiast linijka peers_certfile wskazuje demonowi racoon plik z kluczem publicznym zdalnego partnera - i jest to plik laptop.public. Sekcja proposal pozostaje w niezmienionej formie w stosunku do tego co widzieliśmy wcześniej za wyjątkiem opcji authentication_method którą jest obecnie rsasig - wskazując, że używamy pary prywatnej/publicznej RSA do wykonania uwierzytelnienia. Zmiany w konfiguracji 10.0.0.216 są analogiczne do tych, wykonanych na 10.0.0.11: path certificate "/usr/local/etc/racoon/certs"; remote 10.0.0.11 { exchange_mode aggressive,main; my_identifier asn1dn; peers_identifier asn1dn; certificate_type x509 "laptop.public" "laptop.private"; peers_certfile "upstairs.public"; proposal { encryption_algorithm 3des; hash_algorithm sha1; authentication_method rsasig; dh_group 2 ; } } Teraz, gdy już dodaliśmy odpowiednie opcje na obu hostach, musimy tylko umieścić klucze we właściwych miejscach. Maszyna `upstairs' powinna mieć pliki upstairs.private, upstairs.public, i laptop.public w katalogu /usr/local/etc/racoon/certs. Upewnij się że właścicielem tego katalogu jest root a sam katalog ma prawa dostępu ustawione na 0700 - w przeciwnym wypadku racoon odmówi ich odczytania. Maszyna `laptop' powinna mieć pliki laptop.private, laptop.public i upstairs.public w katalogu /usr/local/etc/racoon/certs. Innymi słowy, każdy host potrzebuje dostęp do swojego klucza prywatnego, publicznego oraz do klucza publicznego hosta z którym chce nawiązać łączność. Sprawdź, czy reguły bezpieczeństwa zostały prawidłowo zdefiniowane (wykonaj polecenia spdadd z [175]sekcja Przykład). Teraz uruchom racoon i wszystko powinno działać. _________________________________________________________________ Jak bezpiecznie budować tunele Aby ustanowić bezpieczną trasę komunikacji ze zdalnym partnerem, musimy wymienić klucze publiczne. O ile same klucze publiczne nie muszą być sekretem, ważne jest by zachować je w niezmienionej formie. Innymi słowy, trzeba mieć pewność że nie dojdzie do ataku typu `człowiek-pośrodku' (ang. "man in the middle"). Aby to ułatwić, w OpenSSL zaimplementowano polecenie digest: $ openssl dgst upstairs.public MD5(upstairs.public)= 78a3bddafb4d681c1ca8ed4d23da4ff1 Teraz wystarczy zweryfikować że nasz zdalny partner ma taką samą sumę kontrolną dla tego pliku. Można to zrobić po prostu spotykając się, lub przez telefon - generalnie chodzi o to, by potwierdzić to inną drogą komunikacji. Niedopuszczalne jest, by zdalna strona wysłała swoją sumę kontrolną również np. mailem! Innym sposobem osiągnięcia tego samego, jest zaufana trzecia osoba/instytucja (ang. "Trusted Third Party") która utrzymuje instytucję certyfikującą (ang. "Certificate Authority", CA). CA podpisze klucz, który w naszym przypadku sami podpisaliśmy i partner będzie mógł w CA zweryfikować że otrzymany od nas klucz jest faktycznie tym, który my pierwotnie zgłosiliśmy do podpisania w CA. _________________________________________________________________ Tunele IPsec Jak dotąd zajmowaliśmy się połączeniami IPsec w tak zwanym trybie `transportowym', w którym oba punkty transmisji rozumieją IPsec. Ponieważ często nie można doprowadzić do takiej sytuacji, można skonfigurować IPsec tylko na routerach i kazać im zestawić bezpieczny tunel dla całej komunikacji z podsieci "za" routerami. Jak można się łatwo domyślić, nazywane jest to właśnie `tunelowaniem' lub po prostu trybem tunelu. Konfiguracja jest prościutka. Aby tunelować cały ruch do podsieci 130.161.0.0/16 z 10.0.0.216 przez 10.0.0.11, wydajemy następujące polecenie na 10.0.0.216: #!/sbin/setkey -f flush; spdflush; add 10.0.0.216 10.0.0.11 esp 34501 -m tunnel -E 3des-cbc "123456789012123456789012"; spdadd 10.0.0.0/24 130.161.0.0/16 any -P out ipsec esp/tunnel/10.0.0.216-10.0.0.11/require; Zauważ że opcja -m tunnel jest bardzo istotna! Linijka konfiguruje SA szyfrujące za pomocą ESP pomiędzy dwoma końcami tuneli - 10.0.0.216 i 10.0.0.11. Następnie konfigurujemy sam tunel. Polecenie instruuje kernel, by szyfrował cały ruch z 10.0.0.0/24 do 130.161.0.0/16. Zaszyfrowany ruch ma być wysyłany do routera 10.0.0.11. Host 10.0.0.11 również wymaga analogicznej konfiguracji: #!/sbin/setkey -f flush; spdflush; add 10.0.0.216 10.0.0.11 esp 34501 -m tunnel -E 3des-cbc "123456789012123456789012"; spdadd 10.0.0.0/24 130.161.0.0/16 any -P in ipsec esp/tunnel/10.0.0.216-10.0.0.11/require; Zauważ że wpisy są prawie identyczne, poza zmianą -P out na -P in. Tak jak w poprzednich przykładach, skonfigurowaliśmy reguły dla ruchu przechodzącego w jedną stronę. Poprawną konfigurację szyfrowania ruchu w drugą stronę pozostawiamy jako ćwiczenie dla czytelnika. Inną nazwą dla takiej konfiguracji jest `proxy ESP', która może choć nie musi być jaśniejsza. Notatka: Tunel IPsec wymaga włączonego przekazywania pakietów IP (ang. "IP Forwarding") w kernelu! _________________________________________________________________ Inne oprogramowanie IPsec Tomasz Walpuski donosi, że napisał łatkę dla isakmpd w OpenBSD, pozwalającą na połączenia z implementacją IPsec w Linuksie 2.5. Co więcej, repozytorium CVS isakmpd zawiera obecnie ten kod! Trochę informacji znajdziecie na [176]tej stronie. isakmpd jest trochę inny niż racoon wspominamy powyżej, ale wiele ludzi lubi go. Można go znaleźć [177]tutaj. Więcej o CVS OpenBSD można przeczytać [178]tutaj. Tomasz udostępnił również gotowe [179]archiwum dla osób niezaznajomionych z CVS lub ideą nakładania łatek. _________________________________________________________________ Współpraca IPsec z innymi systemami FIXME: Napisać to _________________________________________________________________ Windows FIXME: Napisać to _________________________________________________________________ Rozdział 8. Routing multicastowy FIXME: nie ma autora! HOWTO poświęcone temu tematowi jest praktycznie starożytne i może być niedokładne lub mylące. Zanim zajmiesz się routingiem multicastowym, musisz skonfigurować swój kernel. Oznacza to zdecydowanie się na konkretny typ tego routingu. Istnieją cztery główne "wspólne" rodzaje - DVMRP (multicastowa wersja unicastowego protokołu RIP), MOSPF (to samo dla OSPF), PIM-SM ("Protocol Independent Multicasting - Sparse Mode", który zakłada że użytkownicy są mocno rozproszeni) i PIM-DM (to samo, ale w trybie zagęszczonym, zakładającym że grupy użytkowników będą blisko siebie). Przy konfiguracji jądra Linuksa zauważysz zapewne, że nie ma tych opcji. Dzieje się tak, ponieważ sam protokół obsługiwany jest przez aplikację routującą, np. Zebra, mrouted czy pimd. Z drugiej strony musisz wiedzieć, którego protokołu będziesz używać by wybrać w konfiguracji kernela odpowiednie opcje. Dla całego routingu multicastowego, będziesz zdecydowanie musiał zaznaczyć opcję `multicasting' i `multicast routing'. Dla DVMRP i MOSPF to wystarczy. Jeśli będziesz używał PIM, musisz również włączyć PIMv1 lub PIMv2 w zależności od tego, której wersji używa sieć do której będziesz się podłączał. Gdy skonfigurujesz już kernel i skompilujesz go, zauważysz, że lista obsługiwanych protokołów IP wyświetlana podczas startu zawiera również IGMP. Jest to właśnie protokół do zarządzania grupami multicastowymi. W momencie pisania tego tekstu, Linuks wspiera wersję 1 i 2, mimo że wersja 3 jest udokumentowana i istnieje. Nie ma to jednak dużego znaczenia, ponieważ IGMPv3 jest nowe i nowa funkcjonalność nie jest aż tak niezbędna. Ponieważ IGMP zajmuje się grupami, tylko funkcje obsługiwane przez wszystkie implementacje w grupie będą używane. W większości przypadków będzie to funkcjonalność IGMPv2, choć można czasami spotkać gdzieniegdzie również IGMPv1. Jak na razie szło nieźle. Włączyliśmy multicasting. Teraz poinstruujemy kernel Linuksa by coś z tym zrobił tak, byśmy mogli zacząć routowanie. Oznacza to dodanie wirtualnej sieci multicastowej do tabeli rutownania: ip route add 224.0.0.0/4 dev eth0 (zakładając oczywiście, że zamierzasz obsługiwać ruch multicastowy przez interfejs `eth0'! Zamień go w poleceniu na twoje urządzenie) Teraz poinstruujemy Linuksa, by przekazywał pakiety... echo 1 > /proc/sys/net/ipv4/ip_forward W tym momencie możesz zastanawiać się czy to w ogóle do czegoś będzie służyło. By przetestować połączenie, pingnijmy domyślną grupę, 224.0.0.1, by sprawdzić czy ktoś odpowie. Wszystkie komputery w twojej sieci LAN z włączoną obsługą multicastów powinny odpowiedzieć - i nic innego. Zauważysz, że żadna maszyna która odpowiedziała, nie miała adresu 224.0.0.1. Co za niespodzianka! :) Jest to po prostu adres grupowy (rodzaj rozgłoszenia tylko dla prenumeratorów) i wszyscy członkowie grupy odpowiadają swoimi adresami. ping -c 2 224.0.0.1 W tym momencie jesteś gotowy do prowadzenia routingu multicastowego. Zakładając, że masz dwie sieci między którymi możesz to robić. (Będzie kontynuowane!) _________________________________________________________________ Rozdział 9. Dyscypliny kolejkowania dla Zarządzania Pasmem Gdy je odkryłem, naprawdę mnie to rozwaliło. Linuks 2.2/2.4 posiada wszystko potrzebne do zarządzania pasmem i to z funkcjonalnością porównywalną do dedykowanych systemów zarządzania pasmem z górnej półki. Linuks idzie nawet dalej, wykraczając poza to co dają sieci Frame Relay i ATM. By zapobiec nieporozumieniom, tc używa następujących reguł do specyfikacji pasma: mbps = 1024 kbps = 1024 * 1024 bps => bajtów/sekundę mbit = 1024 kbit => kilo bitów/sekundę. mb = 1024 kb = 1024 * 1024 b => bajtów mbit = 1024 kbit => kilo bitów. Wewnętrznie, liczby przechowywane są w bps i b. Ale gdy tc drukuje wartości, używa następujących wartości: 1Mbit = 1024 Kbit = 1024 * 1024 bps => bajtów/sekundę _________________________________________________________________ Kolejki i Dyscypliny Kolejkowania wyjaśnione Dzięki kolejkowaniu, określamy które dane są WYSYŁANE. Ważne jest, byś zrozumiał że możemy jedynie kontrolować w ten sposób dane, które wysyłamy. Z uwagi na taką a nie inną budowę Internetu, nie mamy bezpośredniej kontroli nad tym, co ludzie wysyłają do nas. To trochę jak z twoją fizyczną skrzynką pocztową w domu. Nie ma sposobu by zmusić świat, aby wysyłał ci tylko określoną liczbę poczty bez skontaktowania najpierw ze wszystkimi ludźmi. Internet na szczęście oparty jest głównie o TCP/IP, które ma pewne cechy mogące nam pomóc. TCP/IP nie zna przepustowości sieci pomiędzy dwoma komputerami, więc zaczyna od wysyłania danych coraz szybciej ( "wolny start" ) i kiedy zaczyna gubić pakiety ponieważ nie ma już dla nich miejsca, zwalnia. Tak naprawdę jest to trochę bardziej skomplikowane, ale więcej napiszemy później. Wracając do naszej analogii z fizyczną skrzynką pocztową - to tak jakbyś przestał czytać np. połowę poczty w nadziei, że ludzie których poczty nie czytasz, przestaną w końcu do ciebie pisać. Jedyna różnica to fakt, że działa to dla Internetu :-) Jeśli masz router i chciałbyś zapobiec sytuacji, w której określone komputery ściągają dane za szybko, musisz wprowadzić ograniczenia na wewnętrznym interfejsie routera - tym, który wysyła dane do twoich komputerów. Musisz być również pewien, że kontrolujesz połączenie w najwęższym miejscu. Jeśli masz 100Mbitową kartę sieciową a router ma łącze o przepustowości 256kbitów, musisz upewnić się że nie wysyłasz więcej danych niż router jest w stanie obsłużyć. Jeśli o to nie zadbasz, to router będzie kontrolował połączenie i ograniczał pasmo w mniej lub bardziej przydatny dla nas sposób. Musimy `zawładnąć kolejką' mówiąc po prostu i być najwolniejszym połączeniem w łańcuchu. Jest to na szczęście bardzo łatwe. _________________________________________________________________ Proste, bezklasowe Dyscypliny Kolejkowania Tak jak to już powiedziano, dyscyplinami kolejkowania zmieniamy sposób w jaki dane są wysyłane. Bezklasowe dyscypliny kolejkowania to te, które zajmują się jedynie odbieraniem danych, przesuwaniem ich transmisji w czasie lub ewentualnie odrzucaniem. Mogą zostać użyte do kontroli pasma dla całego interfejsu, bez żadnych dodatkowych podziałów. Bardzo ważne jest, byś zrozumiał tą część kolejkowania zanim zajmiemy się zagadnieniem zagnieżdżonych dyscyplin kolejkowania z klasami ruchu. Najczęściej używaną dyscypliną jest `pfifo_fast' - i jest domyślna. Jej popularność wyjaśnia dlaczego zaawansowane opcje są takie wydajne. Nie zawierają po prostu nic oprócz `kolejnej kolejki'. Każda z tych kolejek ma swoje mocne i słabe strony. Nie wszystkie są też dokładnie przetestowane. _________________________________________________________________ pfifo_fast Kolejka ta, dokładnie tak jak wskazuje na to jej nazwa, to tradycyjne `Pierwszy Wszedł, Pierwszy Wyjdzie' (ang. "First In First Out", FIFO). Oznacza to, że żaden pakiet nie będzie specjalnie traktowany (przynajmniej nie wprost). Kolejka ta ma 3 `pasma' (ang. "band"). W każdym z pasm reguły FIFO działają niezależnie. Jednak jeśli w paśmie 0 są jeszcze pakiety, pasmo 1 nie zostanie obsłużone. Tak samo dzieje się w przypadku pasm 1 i 2. Kernel honoruje tak zwaną flagę `Typu Usługi' (ang. "Type of Service") i zajmuje się ustawianiem opcji `minimalna zwłoka' w pakietach z pasma 0. Nie pomylcie tej bezklasowej kolejki z klasową kolejką PRIO! Mimo, że zachowują się podobnie, pfifo_fast jest bezklasowa i nie można dodawać innych kolejek do niej przy użyciu polecenia tc. _________________________________________________________________ Parametry i ich użycie Nie można konfigurować kolejki pfifo_fast, ponieważ jest ustawiona na sztywno w domyślnej konfiguracji. Poniżej jak to jest zrobione: priomap Określa w jaki sposób priorytety dla pakietów, przydzielane przez kernel, odwzorowywane są na pasma. Odwzorowywanie zachodzi na podstawie oktetu ToS pakietu, który wygląda tak: 0 1 2 3 4 5 6 7 +-----+-----+-----+-----+-----+-----+-----+-----+ | | | | | KOLEJNOŚĆ | TOS | MBZ | | | | | +-----+-----+-----+-----+-----+-----+-----+-----+ Czterobitowe pole ToS definiowane jest w następujący sposób: Binarnie Decymalnie Znaczenie ----------------------------------------- 1000 8 Zminimalizuj zwłokę (md) 0100 4 Zmaksymalizuj przepustowość (mt) 0010 2 Zmaksymalizuj niezawodność (mr) 0001 1 Zminimalizuj koszt (mmc) 0000 0 Normalna usługa Ponieważ na prawo od pola ToS znajduje się jeszcze jeden bit, pole ToS jest równe podwojonej wartości bitów ToS. tcpdump -v -v pokazuje wartość całego pola ToS, nie tylko tych 4 bitów. Jest to wartość, którą możesz znaleźć w pierwszej kolumnie tej tabeli: TOS Bity Znaczenie Priorytet Linuksa Pasmo --------------------------------------------------------------------- 0x0 0 Normalna Usługa 0 Najlepszy efekt 1 0x2 1 Zminimalizuj koszt 1 Wypełniacz 2 0x4 2 Zmaksymalizuj niezawodność 0 Najlepszy efekt 1 0x6 3 mmc+mr 0 Najlepszy efekt 1 0x8 4 Zmaksymalizuj przepustowość 2 Większość 2 0xa 5 mmc+mt 2 Większość 2 0xc 6 mr+mt 2 Większość 2 0xe 7 mmc+mr+mt 2 Większość 2 0x10 8 Zminimalizuj zwłokę 6 Interaktywnie 0 0x12 9 mmc+md 6 Interaktywni 0 0x14 10 mr+md 6 Interaktywni 0 0x16 11 mmc+mr+md 6 Interaktywni 0 0x18 12 mt+md 4 Większość interakt.1 0x1a 13 mmc+mt+md 4 Większość interakt.1 0x1c 14 mr+mt+md 4 Większość interakt.1 0x1e 15 mmc+mr+mt+md 4 Większość interakt.1 Dużo numerków. Druga kolumna zawiera wartość czterech bitów pola ToS, a następnie ich znaczenie. Na przykład wartość 15 oznacza pakiet wymagający Minimalnego Kosztu, Maksymalnej Niezawodności, Maksymalnej Przepustowości i Minimalnej Zwłoki. Nazwałbym go `Pakietem Holenderskim'. Czwarta kolumna to sposób w jaki kernel interpretuje bity ToS, określony priorytetem, który jest im przyznawany. Ostatnia kolumna to wynik domyślnej mapy priorytetów (priomap). W linii poleceń, wygląda ona tak: 1, 2, 2, 2, 1, 2, 0, 0 , 1, 1, 1, 1, 1, 1, 1, 1 Oznacza to na przykład dla priorytetu 4, że mapowany jest na pasmo 1. Mapa priorytetów pozwala również na wylistowanie wyższych priorytetów (> 7), które nie odpowiadają mapowaniom ToS, ale są rozsyłane dla innych zastosowań. Poniższy wydruk pochodzi z RFC 1349 (przeczytaj ten dokument jeśli chcesz zapoznać się ze szczegółami) i omawia jak aplikacje mogłyby ustawiać pole TOS swoich pakietów: TELNET 1000 (zminimalizuj zwłokę) FTP kanał kontroli 1000 (zminimalizuj zwłokę) Dane 0100 (zmaksymalizuj przepustowość) TFTP 1000 (zminimalizuj zwłokę) SMTP Faza komend 1000 (zminimalizuj zwłokę) Faza danych 0100 (zmaksymalizuj przepustowość) Domain Name Service Zapytanie UDP 1000 (zminimalizuj zwłokę) Zapytanie TCP 0000 Transfer Strefy 0100 (zmaksymalizuj przepustowość) NNTP 0001 (zminimalizuj koszt) ICMP Błędy 0000 Zapytania 0000 (zwykle) Odpowiedzi (zwykle) txqueuelen Długość tej kolejki odpowiada konfiguracji interfejsu. Można ją sprawdzić i ustawić posługując się poleceniami ifconfig i ip. By ustawić długość kolejki na 10, wykonaj polecenie ifconfig eth0 txqueuelen 10. Nie można ustawić wartości tego parametru za pomocą tc! _________________________________________________________________ Token Bucket Filter Token Bucket Filter (TBF, w wolnym tłumaczeniu "Filtr Wiadra Żetonów") to prosta kolejka z dyscypliną, która przepuszcza tylko dane przychodzące z pewną częstotliwością nie przekraczającą nałożonych ograniczeń, ale z możliwością przyjęcia krótkich serii danych które przekraczają tych ograniczeń. TBF jest bardzo precyzyjna, przyjazna zarówno dla sieci jak i procesora. Powinna być twoim pierwszym wyborem jeśli chcesz po prostu zwolnić interfejs! Implementacja TBF składa się z bufora (`wiadra', ang. "bucket"), wypełnianego pewnymi wirtualnymi porcjami danych (`żetonami', ang. "token") z określoną częstotliwością. Najważniejszym parametrem wiadra jest jego rozmiar, określający ilość żetonów które może ono przechować. Każdy nadchodzący żeton wyjmuje jeden przychodzący pakiet z kolejki danych i jest kasowany z wiadra. Skojarzenie tego algorytmu z dwoma przepływami - żetonów i danych, daje trzy możliwe scenariusze: * Dane docierają do TBF z częstotliwością równą częstotliwości napływania żetonów. W tym przypadku każdy przychodzący pakiet ma swój żeton i przechodzi przez kolejkę bez zwłoki. * Dane docierają do TBF z częstotliwością mniejszą niż częstotliwość napływania żetonów. Tylko ich część jest używana, więc te puste zbierane są do momentu osiągnięcia rozmiaru `wiadra'. Mogą być potem użyte do krótkiej serii pakietów przekraczających normalny limit przepustowości. * Dane docierają do TBF z częstotliwością większą niż częstotliwość napływania żetonów. Oznacza to, że `wiadro' zostanie w końcu opróżnione z pustych żetonów, powodując przez moment zwiększenie przepustowości. Nazywa się to `przekroczeniem limitu' i jeśli pakiety nadal będą napływały z niezmienną częstotliwością, niektóre będą odrzucane. Ostatni scenariusz jest bardzo ważny, ponieważ umożliwia kształtować przepustowość dostępną dla danych które przechodzą przez filtr. Akumulacja żetonów zapewnia pewnego rodzaju zapas bezpieczeństwa, który wykorzystany zostanie gdy pakiety zaczną nagle docierać z większą niż dotychczas częstotliwością. W końcu jednak, ciągłe obciążenie większe od maksymalnego zdefiniowanego spowoduje przeładowanie i pakiety zaczną być najpierw opóźniane, a w końcu odrzucane. Proszę zauważyć, że w implementacji programowej, żetony odpowiadają bajtom a nie pakietom. _________________________________________________________________ Parametry i ich użycie Mimo, że zapewne nie będziesz potrzebował ich zmieniać, tbf udostępnia parę zmiennych kontrolnych. Po pierwsze, parametry dostępne zawsze: limit lub latency (limit lub opóźnienie) Limit to numer bajtów, które mogą zostać skolejkowane w oczekiwaniu na wolne żetony. Możesz również określić parametr wskazując opóźnienie, określające maksymalny czas jaki pakiet może spędzić w TBF. To drugie bierze pod uwagę rozmiar `wiadra', częstotliwość i ewentualnie (jeśli zostanie ustawiona) częstotliwość szczytową. burst/buffer/maxburst Rozmar `wiadra', w bajtach. Jest to maksymalna ilość bajtów dla których jednocześnie mogą być dostępne żetony. Generalnie, kształtowanie ruchu większych przepustowości wymaga większego bufora. Dla ruchu 10mbit/s na karcie Intel, potrzebujesz przynajmniej 10 kilobajtowego bufora, jeśli chcesz osiągnąć żądaną przepustowość! Jeśli bufor będzie za mały, pakiety mogą być odrzucane dlatego, że przepływa więcej żetonów na jednostkę zegara, niż mieści się w `wiadrze'. mpu Pakiet długości zero bajtów nie zużywa zero przepustowości. Dla ethernetu, pusty pakiet zużywa mniej niż 64 bajty. `Minimalna Jednostka Pakietu' (ang. "Minimum Packet Unit") określa minimalną zajętość żetona przez pakiet. rate Ustawienie prędkości. Zajrzyj do notatek powyżej o limitach! Jeśli `wiadro' zawiera żetony i nie może być puste, domyślnie pracuje z nieograniczoną prędkością. Jeśli jest to nieakceptowalne, użyj następujących parametrów: peakrate Domyślnie, jeśli dostępne są żetony a przybywają pakiety, są one natychmiast wysyłane. Może to nie być konkretnie to, czego chcesz, szczególnie jeśli masz duże `wiadro'. Parametr `peakrate' może zostać użyty do określenia jak szybko `wiadro' może być opróżniane. Jeśli go określisz, po wysłaniu pakietu odczekamy chwilę i dopiero po jej upływie wyślemy następny - dzięki przeliczeniu przestrzeni czasowej między pakietami tak, aby wysyłać dokładnie tyle ile wynosi wartość tego parametru. Jednak ponieważ zegar Unixowy ma rozdzielczość tylko 10ms, otrzymując 10.000 bitów średniego ruchu ograniczeni jesteśmy do `peakrate' równej około 1mbit/s! mtu/minburst Oczywiście `peakrate' równe 1mbit/s nie jest zbyt użyteczne, jeśli twój normalny ruch wynosi więcej. Większa wartość szczytowa możliwa jest do osiągnięcia przez wysyłanie większej ilości pakietów na jedną jednostkę zegara, co oznacza, że tworzymy drugie wiadro! To drugie wiadro domyślnie ma wielkość jednego pakietu, więc tak naprawdę nie jest wcale wiadrem. By wyliczyć maksymalną wartość szczytową, pomnóż skonfigurowane MTU przez 100 (lub, bardziej poprawnie, HZ, co oznacza 100 na platformie intelowskiej i 1024 na Alfie). _________________________________________________________________ Przykładowa konfiguracja Prosta, ale bardzo przydatna konfiguracja to: # tc qdisc add dev ppp0 root tbf rate 220kbit latency 50ms burst 1540 No dobra, dlaczego jest taka przydatna? Jeśli masz urządzenie sieciowe z całkiem dużą kolejką, tak jak na przykład modem kablowy lub DSL i łączysz je do szybkiego urządzenia takiego jak na przykład interfejs ethernetowy, zauważysz od razu, że wrzucanie czegoś od razu niszczy interaktywność. Dzieje się tak dlatego, ponieważ wysyłanie pakietów zapełni kolejkę w modemie, która jest prawdopodobnie bardzo duża - taka konfiguracja pomaga uzyskać dużą przepustowość. Ale to nie jest to co chcesz osiągnąć, chcesz mieć pełną interaktywność a nie zapchać modem jednym strumieniem i nie móc zrobić już nic innego. Linia powyżej spowalnia wysyłanie do częstotliwości, która nie prowadzi zapełniania kolejki w modemie - kolejka będzie już na Linuksie, gdzie mamy swobodę konfiguracji jej do określonego rozmiaru. Zmień wartość 220kbit na swoją faktyczną prędkość wysyłania danych minus parę procent. Jeśli masz naprawdę bardzo szybki modem, możesz troszkę zwiększyć wartość `burst'. _________________________________________________________________ Sprawiedliwe Kolejkowanie Stochastyczne (ang. "Stochastic Fairness Queueing") Stochastic Fairness Queueing (SFQ) to prosta implementacja rodziny algorytmów ze sprawiedliwym podziałem pasma. Jest mniej dokładna niż inne, ale wymaga również mniejszej ilości wyliczeń. Kluczowym pojęciem w SFQ jest konwersacja (lub potok), która odpowiada zwykle sesji TCP lub strumieniowi UDP. Ruch dzielony jest w dużą ilość kolejek FIFO, po jednej na każdą konwersację. Następnie każda z kolejek opróżniana jest na zasadzie `round-robin' tak aby każda z sesji mogła wysłać swoje dane. Zapewnia to sprawiedliwą pracę i uniemożliwia jednej konwersacji zajęcie całego pasma. SFQ nazywana jest `stochastyczną', ponieważ tak naprawdę nie przydziela kolejki dla każdej sesji, a używa procedury dzielącej ruch na ograniczoną liczbę kolejek przy pomocy algorytmu `mieszającego' (ang. "hashing"). Ponieważ używana jest wartość mieszająca ( wspominany hash ), może dojść do sytuacji w której więcej niż jedna sesja może skończyć w tym samym wiadrze. Oznaczałoby to w praktyce zmniejszenie szansy wysłania pakietu o połowę i podzielenie tym samym na pół dostępnej efektywnej prędkości. By temu zapobiec, SFQ zmienia często algorytm mieszający i nawet jeśli dojdzie do kolizji sesji będzie to działo się tylko przez parę sekund. Warto zauważyć, że SFQ jest użyteczne w przypadku gdy twój interfejs wychodzący jest naprawdę pełen! Jeśli nie będzie, nie stworzona zostanie kolejka i tak naprawdę nie będzie miało to żadnego efektu. Później omówimy jak połączyć SFQ z innymi dyscyplinami kolejkowania i uzyskać to co najlepsze z obu sytuacji. W szczególności, zastosowanie SFQ wobec interfejsu ethernetowego podłączonego do modemu kablowego lub rutera DSL jest bezcelowe, bez dalszego nałożenia ograniczeń! _________________________________________________________________ Parametry i użycie SFQ jest praktycznie samokonfigurowalna: perturb Wartość określajaca co ile sekund zmieniany będzie algorytm mieszający. Jeśli nie zostanie podana, wyliczona wartość mieszająca nie będzie rekonfigurowana. Generalnie pomijanie tego parametru nie jest zalecane - wartość 10 sekund wydaje się dobrym wyborem. quantum Ilość bajtów, którą strumień może zdjąć z kolejki zanim szansę wysłania otrzyma kolejna kolejka. Domyślnie ustawione jest na równowartość maksymalnej jednostki transmisji 1 pakietu (MTU). Nie ustawiaj tego parametru poniżej MTU! _________________________________________________________________ Przykładowa konfiguracja Jeśli masz urządzenie połączone łączem o identycznej przepustowości jaka jest ogólnie dostępna, tak jak na przykład w sytuacji połączenia modemowego, to polecenie pomoże ci uwydatnić sprawiedliwy podział ruchu: # tc qdisc add dev ppp0 root sfq perturb 10 # tc -s -d qdisc ls qdisc sfq 800c: dev ppp0 quantum 1514b limit 128p flows 128/1024 perturb 10sec Sent 4812 bytes 62 pkts (dropped 0, overlimits 0) Numer `800c:' to automatycznie nadawana etykietą, limit oznacza, że w kolejce oczekiwać może 128 pakietów. Dostępne są 1024 wiadra kontrolowane algorytmem mieszającym, z których 128 może być jednocześnie aktywnych (więcej pakietów nie wejdzie do kolejki!). Co 10 sekund, wartości mieszające są rekonfigurowane. _________________________________________________________________ Kiedy używać której kolejki Podsumowując, powyższe algorytmy tworzą proste kolejki zarządzające ruchem przez rekolejkowanie, zwalnianie lub odrzucanie pakietów. Poniższe podpowiedzi mogą pomóc w wyborze odpowiedniej kolejki. Wspominają one niektóre dyscypliny kolejkowania omówione w rozdziale [180]Rozdział 14. * Jeśli chcesz zwolnić ruch wychodzący, użyj TBF. Działa nawet dla dużych przepustowości, jeśli odpowiednio wyskalujesz wiadro. * Jeśli masz naprawdę zapchane łącze i chcesz mieć pewność, że żadna sesja nie zdominuje całego pasma wychodzącego, użyj SFQ. * Jeśli masz naprawdę dużą sieć szkieletową i wiesz co robisz, rozważ algorytm RED (opisany w rozdziale zaawansowanym). * By kształtować (ang. "shape") ruch przychodzący, którego nie przekazujesz gdzie indziej, użyj `Polityki dla ruchu przychodzącego' (ang. "Ingress Policer"). Nawiasem mówiąc, kontrolowanie ruchu przychodzącego to `określanie polityki' (ang. "policing") a nie `kształtowanie'. * Jeśli go jednak przekazujesz, używaj TBF na interfejsie wysyłającym te dane, chyba, że może on wychodzić przez wiele interfejsów. W tym wypadku jedyną wspólną cechą będzie interfejs którym ruch dotarł i wtedy należy użyć polityki dla ruchu przychodzącego. * Jeśli nie chcesz kształtować ruchu, a jedynie sprawdzić czy twój interfejs jest tak obciążony, że musi używać swojej kolejki, użyj kolejkowania `pfifo' (nie `pfifo_fast'). Brakuje jej ograniczeń, ale zlicza parametry pracy. * Na koniec - możesz użyć "kształtowania osobistego". Nie zawsze będziesz w stanie użyć dostępnej technologii do osiągnięcia dokładnie założonego celu, a użytkownicy zwykle odczuwają nakładane ograniczenia jako złośliwość. Przyjazne słowo może pomoć w osiągnięciu odpowiednich parametrów pracy w sieci! _________________________________________________________________ Terminologia By prawidłowo zrozumieć bardziej zaawansowane konfiguracje, niezbędne jest prawidłowe wytłumaczenie pewnych koncepcji. Z uwagi na poziom skomplikowania i świeżość tematu, wiele ludzi używa różnych słów na oznaczanie tych samych rzeczy. Poniższa lista bazuje na zawartości dokumentu draft-ietf-diffserv-model-06.txt zatytułowanego An Informal Management Model for Diffserv Routers. Można go znaleźć pod adresem [181]http://www.ietf.org/internet-drafts/draft-ietf-diffserv-model-06. txt. Zapoznaj się z dokumentem by poznać dokładne definicje terminów, które będziemy używali. Dyscyplina kolejkowania (ang. "Queueing Discipline", qdisc) Algorytm zarządzający kolejką urządzenia, dla ruchu `przychodzącego' (ang. "ingrees") lub `wychodzącego' (ang. "egrees"). root qdisc Główna qdisc to dyscyplina dołączona bezpośrednio do urządzenia. Bezklasowa dyscyplina kolejkowania (ang. "Classless qdisc") Qdisc w której nie ma konfigurowalnych wewnętrznych podziałów. Dyscyplina kolejkowania z klasami (ang. "Classful qdisc") Dyscyplina kolejkowania może zawierać wiele klas. Każda z nich może zawierać następne dyscypliny kolejkowania i tak dalej i tak dalej - ale nie musi. Zgodnie z definicją, `pfifo_fast' jest dyscypliną kolejkowania z klasami - zawiera trzy pasma będące klasami. Ponieważ jednak żadne jej parametry nie mogą być modyfikowane narzędziem tc, z punktu widzenia użytkownika jest bezklasowa. Klasy (ang. "Classes") Dyscyplina kolejkowania z klasami, tak jak wskazuje jej nazwa, zawiera klasy. Mogą one zawierać kolejne dyscypliny i tak dalej i tak dalej. Innymi słowy, możesz skonfigurować qdisc która będzie rodzicem dla innej qdisc. Liść to qdisc, która nie posiada dzieci. Klasa ma przydzieloną jedną dyscyplinę kolejkowania i jest ona odpowiedzialna za wysyłanie danych z tej klasy. Gdy tworzysz klasę, przydzielana jest do niej domyślnie dyscyplina FIFO. Gdy dodasz dziecko do dyscypliny, FIFO jest usuwana. Dla klasy będącej liściem, algorytm FIFO można zastąpić bardziej odpowiednią dyscypliną kolejkowania. Możesz nawet ją zastąpić dyscypliną kolejkowania z klasami, co pozwoli ci na dalszą rozbudowę klas. Klasyfikator (ang. "Classifier") Każda dyscyplina kolejkowania z klasami musi wiedzieć, do której klasy ma wysłać pakiet. Wykonuje to właśnie na podstawie klasyfikatora. Filtr (ang. "Filter") Klasyfikacja może zostać wykonana na podstawie filtrów. Filtr zawiera pewną liczbę warunków, które jeśli pasują, sprawiają że filtr również pasuje. Planowanie (ang. "Scheduling") Dyscyplina kolejkowania z klasami, z pomocą klasyfikatora może zdecydować, że niektóre pakiety powinny zostać wysłane wcześniej niż inne. Proces ten nazywamy planowaniem i jest wykonywany na przykład przez qdisc `pfifo_fast', o której wspomniano wcześniej. Planowanie nazywa się również `porządkowaniem', ale jest to raczej mylące. Kształtowanie (ang. "Shaping") Proces odwlekania momentu wysłania pakietu tak, by pasował do zdefiniowanych charakterystyk ruchowych i jego maksymalnych wartości. Kształtowanie ruchu wykonywane jest przy opuszczaniu przez pakiet systemu. Czasami kształtowanie oznacza również odrzucanie pakietów zamiast opóźniania ich wysłania. Narzucanie polityki (ang. "Policing") Proces odwrotny do kształtowania, wykonywany na ruchu przychodzącym. Może on tylko odrzucać pakiety a nie odwlekać ich obsługę - nie istnieje `kolejka dla ruchu przychodzącego'. Kolejka bezstratna (ang. "Work-Conserving") Ta qdisc zawsze dostarcza pakiet, jeśli tylko jest dostępny. Innymi słowy, nigdy nie odwleka wysłania pakietu, jeśli urządzenie sieciowe jest gotowe do przyjęcia go (w przypadku ruchu wychodzącego). Kolejka stratna (ang. "non-Work-Conserving") Niektóre kolejki, takie jak na przykład TBF, mogą być zmuszone przytrzymać pakiet przez pewien czas, by dopasować się do ustalonego limitu na przepustowość. Oznacza to, że czasami nie wyślą pakietu, mimo że są już do tego gotowe. Dobrze, przebrnęliśmy przez terminologię, zobaczmy teraz gdzie te wszystkie rzeczy się znajdują. Programy przestrzeni użytkownika (userspace) ^ | +---------------+-----------------------------------------+ | Y | | -------> Stos IP | | | | | | | Y | | | Y | | ^ | | | | / ----------> Przekazywanie -> | | ^ / | | | |/ Y | | | | | | ^ Y /-qdisc1-\ | | | Klasyfikator /--qdisc2--\ | --->->Przychodząca dla ruchu ---qdisc3---- | -> | Qdisc wychodzącego \__qdisc4__/ | | \-qdiscN_/ | | | +----------------------------------------------------------+ Dziękuje Jamal Hadi Salim za ten diagram ASCII. Duży kwadrat reprezentuje kernel. Lewa strzałka reprezentuje ruch docierający do twojego komputera z sieci. Zostaje on następnie wrzucony do klasyfikatora ruchu przychodzącego, który może zastować filtry i być może odrzucić ten ruch. Nazywamy to `narzucaniem polityki'. Dzieje się to we wczesnym stadium, zanim pakiet obejrzał sobie większość kernela. Jest to więc dobre miejsce by odrzucać ruch bez zbędnego angażowania cykli CPU. Jeśli pakiet zostanie przepuszczony, może być skierowany do aplikacji działającej lokalnie - trafia wtedy na stos IP by zostać przetworzony a potem do tej konkretnej aplikacji. Pakiet może jednak być przekazywany dalej, trafia wtedy do klasyfikatora dla ruchu wychodzącego. Programy przestrzeni użytkownika wysyłające pakiety również dostarczają danych dla klasyfikatora ruchu wychodzącego. Ruch wychodzący z takiego czy innego źródła trafia następnie do przetworzenia w określonej liczbie skonfigurowanych qdisc. W domyślnym, nieskonfigurowanym stanie, istnieje jedynie jedna kolejka dla ruchu wychodzącego - jest to `pfifo_fast', która zawsze odbiera pakiet. Nazywamy to `kolejkowaniem'. Pakiet czeka zatem w qdisc na decyzję kernela powodującą wysłanie go przez któryś interfejs sieciowy. Nazywamy to z kolei `opróżnianiem kolejki'. Rysunek ten ma zastosowanie również dla sytuacji z jednym urządzeniem sieciowym - strzałki skierowane do i od kernela nie powinny być brane zbyt dosłownie. W każdym urządzeniu sieciowym obsługiwane jest zarówno odebranie pakietu jak i jego wysłanie. _________________________________________________________________ Dyscypliny kolejkowania z klasami qdisc z klasami przydatne są jeśli masz ruch różnego rodzaju, który powinien być w różny sposób traktowany. Jedną z qdisc z klasami jest `CBQ' - `Kolejkowanie oparte o klasy' (ang. "Class Based Queueing") i jest tak popularna, że ludzie identyfikują całe zagadnienie kolejkowania jako właśnie CBQ, ale nie jest to do końca poprawne. CBQ jest jedynie najstarszą zabawką - a jednocześnie najbardziej skomplikowaną. Nie zawsze może robić to co chciałeś. Może to być szok szczególnie dla ludzi podatnych na `efekt sendmaila' - który uczy nas, że skomplikowane technologie dostarczane bez dokumentacji muszą być najlepszymi dostępnymi. Więcej o CBQ i jego alternatywach już niedługo. _________________________________________________________________ Przepływ w qdisc z klasami Ruch docierający do qdisc z klasami, musi zostać rozesłany do klas podrzędnych, czyli `sklasyfikowany'. By określić co zrobić z konkretnym pakietem używa się filtrów. Ważne jest, by zauważyć że to filtry wywoływane są w qdisc a nie odwrotnie! Filtry dołączone do określonych qdisc odpowiadają decyzją i kolejka używa jej do skolejkowania pakietu w jedną z klas. Każda podklasa może sprawdzić swoje filtry by ewentualnie dopasować się do jeszcze innych założeń czy instrukcji. Jeśli tego nie robi, klasa kolejkuje pakiet do qdisc, ją zawierającą. Poza zawieraniem w sobie innych kolejek, większość qdisc z klasami wykonuje również kształtowanie. Dzięki temu można zarówno planować pakiety (np. przy użyciu SFQ) i ograniczać częstotliwość z jaką będą wysyłane. Potrzebujesz takiej możliwości gdy masz szybki interfejs (na przykład, ethernet) i wolne urządzenie sieciowe (np. modem kablowy). Gdybyśmy uruchomili tylko SFQ nic się nie stanie, ponieważ pakiety docierają i opuszczają twój router bez żadnej zwłoki: interfejs wyjściowy jest dużo szybszy niż faktyczna przepustowość łącza. Nie ma więc kolejki, którą możnaby kontrolować. _________________________________________________________________ Rodzina kolejek z klasami: korzenie, uchwyty, rodzeństwo i rodzice Każdy interfejs ma jedną wychodzącą kolejkę-korzeń, domyślnie jest to wspomniana wcześniej bezklasowa `pfifo_fast'. Każdej z qdisc można przydzielić uchwyt, który będzie potem używany przez polecenia konfigurujące by odwołać się do niej. Poza kolejkami dla ruchu wychodzącego, interfejs może mieć również przypisaną politykę dla ruchu przychodzącego. Uchwyt kolejki składa się z dwóch części: starszego i młodszego numeru. Charakterystyczną nazwą dla kolejki-korzenia jest `1:', które równe jest `1:0'. Młodszy numer kolejki z dyscypliną zawsze równy jest 0. Klasy muszą mieć ten sam starszy numer, określający ich rodzica. Musi być on unikalny w danej konfiguracji qdisc dla ruchu wychodzącego lub przychodzącego. Młodszy numer musi być unikalny w obrębie qdisc i jej dzieci. _________________________________________________________________ Jak filtry używane są do klasyfikowania ruchu Reasumując, typowa hierarchia może wyglądać tak: 1: root qdisc | 1:1 klasa-dziecko / | \ / | \ / | \ / | \ 1:10 1:11 1:12 klasy-dzieci | | | | 11: | klasa-liść | | 10: 12: qdisc / \ / \ 10:1 10:2 12:1 12:2 klasy-liście Ale niech to drzewko cię nie zmyli! Nie powinieneś wyobrażać sobie kernela w wierzchołku tego drzewa z siecią poniżej, to po prostu nie tak. Pakiety są kolejkowane i usuwane z kolejki w korzeniu, który jest jedyną kolejką z którą rozmawia kernel. Pakiet może być sklasyfikowany w następującym łańcuchu: 1: -> 1:1 -> 1:12 -> 12: -> 12:2 Pakiet znajduje się w kolejkce należącej do qdisc dołączonej do klasy 12:2. W tym przykładzie, filtr został dołączony do każdego `węzła' w drzewie, gdzie decyduje do której gałęzi pakiet zostanie skierowany dalej. Może to mieć sens. Jednak możliwe jest również: 1: -> 12:2 W tym przypadku, filtr dołączony do korzenia zdecydował, by wysłać pakiet od razu do 12:2. _________________________________________________________________ Jak pakiety są zdejmowane z kolejki przez sprzęt Kiedy kernel zdecyduje, że musi zdjąć pakiety z kolejki i wysłać je interfejsem, kolejka-korzeń 1: odbiera żądanie rozkolejkowania, które przesyłane jest do `1:1', następnie do `10:', `11:' i `12:' - z których każde odpytuje swoje rodzeństwo i próbuje wykonać na nich funkcję dequeue(). W tym przypadku, kernel musi przejść całe drzewo, ponieważ tylko 12:2 zawiera pakiet. Krótko mówiąc, zagnieżdżone klasy rozmawiają TYLKO ze swoimi rodzicami, nigdy z interfejsami. Tylko kolejka-korzeń jest rozkolejkowywana przez kernel! Wynikiem tego jest fakt, że klasy nigdy nie są rozkolejkowywane szybciej niż na to pozwalają ich rodzice. I to jest dokładnie to czego chcemy - możemy dzięki temu mieć kolejkowanie SFQ w klasie przychodzącej, która nie wykonuje kształtowania a jedynie planowanie; możemy również kształtować ruch w kolejkach wychodzących, które wykonują kształtowanie. _________________________________________________________________ qdisc PRIO Kolejka PRIO nie zajmuje się tak naprawdę kształtowaniem ruchu, dzieli jedynie ruch na podstawie tego, jak skonfigurowałeś filtry. Możesz traktować ją jak `pfifo_fast' na sterydach - zamiast pasma jest osobna klasa zamiast prostej kolejki FIFO. Kiedy pakiet zostaje skolejkowany w qdisc PRIO, na podstawie komend filtrujących które podałeś wcześniej, wybierana jest klasa. Domyślnie, stworzone są trzy i zawierają czyste kolejki FIFO bez żadnej struktury wewnętrznej, ale możesz zastąpić je dowolnymi dostępnymi qdisc. Zawsze gdy pakiet musi zostać wyjęty z kolejki, sprawdza się klasę :1. Wyższe klasy są sprawdzane tylko wtedy, gdy poprzednie nie zwróciły pakietu. Kolejka PRIO jest bardzo użyteczna, gdy chcesz prioretyzować określone rodzaje ruchu nie tylko na podstawie flag ToS, ale całej potęgi, którą zapewniają filtry tc. Może również zawierać inne kolejki do predefiniowanych 3, podczas gdy `pfifo_fast' jest ograniczona do prostych kolejek FIFO. Ponieważ kolejka ta nie zajmuje się kształtowaniem ruchu, obowiązuje to samo ostrzeżenie co do SFQ: używaj jej tylko jeśli fizyczne łącze jest naprawdę zapchane, lub wsadź ją do qdisc z klasami, która nie zajmuje się kształtowaniem ruchu. To ostatnie oznacza praktycznie wszystkie modemy kablowe i urządzenia DSL. Formalnie rzecz biorąc, kolejka PRIO jest planującą kolejką bezstratną. _________________________________________________________________ Parametry PRIO i ich użycie Następujące parametry rozpoznawane są przez tc: bands - pasma Ilość pasm do utworzenia. Każde pasmo to tak naprawdę klasa. Jeśli zmienisz ten numer, musisz zmienić również: priomap - mapa priorytetów Jeśli nie dostarczysz filtrów tc do klasyfikowania ruchu, kolejka PRIO będzie sprawdzać priorytety TC_PRIO by stwierdzić, jak kolejkować ruch. Działa to tak jak kolejka `pfifo_fast' wspomniana wcześniej. Pasma są klasami i domyślnie nazywane są od `numer_starszy:1' do `numer_starszy:3', więc jeśli twoja kolejka PRIO nazywa się 12:, użyj tc by przefiltrować ruch na 12:1 dla podniesienia jej priorytetu. Powtarzam, że pasmo 0 trafia na młodszy numer 1! A pasmo 1 na młodszy numer 2 i tak dalej! _________________________________________________________________ Przykładowa konfiguracja Stworzymy takie drzewko: 1: root qdisc / | \ / | \ / | \ 1:1 1:2 1:3 klasy | | | 10: 20: 30: qdiscs qdiscs sfq tbf sfq pasmo 0 1 2 `Ciężki' ruch pójdzie do 30:, ruch interaktywny do 20: lub 10:. Polecenia do wpisania: # tc qdisc add dev eth0 root handle 1: prio ## To *od razu* tworzy klasy 1:1, 1:2, 1:3 # tc qdisc add dev eth0 parent 1:1 handle 10: sfq # tc qdisc add dev eth0 parent 1:2 handle 20: tbf rate 20kbit buffer 1600 limit 3000 # tc qdisc add dev eth0 parent 1:3 handle 30: sfq Teraz sprawdźmy co stworzyliśmy: # tc -s qdisc ls dev eth0 qdisc sfq 30: quantum 1514b Sent 0 bytes 0 pkts (dropped 0, overlimits 0) qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms Sent 0 bytes 0 pkts (dropped 0, overlimits 0) qdisc sfq 10: quantum 1514b Sent 132 bytes 2 pkts (dropped 0, overlimits 0) qdisc prio 1: bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 Sent 174 bytes 3 pkts (dropped 0, overlimits 0) Jak widzisz, pasmo 0 dostało już trochę ruchu i jeden pakiet został wysłany w czasie wykonywania tego polecenia! Użyjemy teraz trochę transferu danych narzędziem, które poprawnie ustawia flagi ToS i spojrzymy jeszcze raz: # scp tc ahu@10.0.0.11:./ ahu@10.0.0.11's password: tc 100% |*****************************| 353 KB 00:00 # tc -s qdisc ls dev eth0 qdisc sfq 30: quantum 1514b Sent 384228 bytes 274 pkts (dropped 0, overlimits 0) qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms Sent 2640 bytes 20 pkts (dropped 0, overlimits 0) qdisc sfq 10: quantum 1514b Sent 2230 bytes 31 pkts (dropped 0, overlimits 0) qdisc prio 1: bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 Sent 389140 bytes 326 pkts (dropped 0, overlimits 0) Widać, że cały ruch poszedł do uchwytu 30:, który identyfikuje pasmo z najmniejszym priorytetem, tak jak chcieliśmy. Żeby sprawdzić, czy ruch interaktywny faktycznie trafia do wyższych pasm, wygenerujemy trochę takiego ruchu: # tc -s qdisc ls dev eth0 qdisc sfq 30: quantum 1514b Sent 384228 bytes 274 pkts (dropped 0, overlimits 0) qdisc tbf 20: rate 20Kbit burst 1599b lat 667.6ms Sent 2640 bytes 20 pkts (dropped 0, overlimits 0) qdisc sfq 10: quantum 1514b Sent 14926 bytes 193 pkts (dropped 0, overlimits 0) qdisc prio 1: bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 Sent 401836 bytes 488 pkts (dropped 0, overlimits 0) Zadziałało - cały dodatkowy ruch trafił do 10:, która jest naszą kolejką o najwyższym priorytecie. Żaden ruch nie trafił do niższych priorytetów, które otrzymały przed chwilą cały ruch wygenerowany przez `scp'. _________________________________________________________________ Sławna qdisc CBQ Tak jak wspomniano wcześniej, kolejka CBQ jest najbardziej skomplikowaną, tą o której jest najwięcej szumu a jednocześnie najmniej ludzi rozumie co robi i jak skonfigurować ją dokładnie tak jak chcemy. Nie dzieje się tak dlatego, że autorzy byli złośliwi lub niekompetentni - przeciwnie, po prostu algorytm CBQ nie jest zbyt precyzyjny i niezbyt pasuje do sposobu w jaki działa Linuks. Poza byciem kolejką z klasami, CBQ zajmuje się również kształtowaniem ruchu i to właśnie tego nie robi zbyt dobrze. Powinna pracować w ten sposób: jeśli chcesz ograniczyć ruch 10mbit/s do 1mbit/s, łącze powinno być przez 90% bezczynne. Jeśli nie jest, musimy je dławić by BYŁO w 90% czasu faktycznie bezczynne. Jest to raczej trudne do zmierzenia, więc CBQ pobiera czas bezczynności z ilości mikrosekund pomiędzy wywołaniami sprzętowymi o więcej danych. Po połączeniu tych informacji, CBQ aproksymuje jak bardzo łącze jest zajęte. Nie jest to raczej ostrożne i nie zawsze prowadzi do poprawnych rezultatów. Na przykład, jaka jest prędkość łącza na interfejsie, który nie potrafi wysłać pełnych 100mbit/s danych - być może z powodu źle zaimplementowanego sterownika? Karty sieciowe PCMCIA również nigdy nie osiągają 100mbit/s ponieważ nie projektowano do tego tej szyny i ponownie - jak skalkulować czas bezczynności? Robi się jeszcze gorzej, jeśli rozważymy trochę nietypowe urządzenia sieciowe, takie jak PPP ponad Ethernetem czy PPTP ponad TCP/IP. Efektywna przepustowość w tym przypadku to tak naprawdę miara tego jak dobrze działają `rurki' (ang. "pipe") do przestrzeni użytkownika - a działają bardzo wydajnie. Ludzie, którzy prowadzili pomiary stwierdzili, że CBQ nie zawsze jest bardzo dokładne a czasami w ogóle podaje nieadekwatne rezultaty. W większości wypadków, działa jednak dobrze. Z dokumentacją, którą masz w ręku, powinieneś poradzić sobie ze skonfigurowaniem jej tak, by działała dobrze. _________________________________________________________________ Szczegóły kształtowania ruchu przez CBQ Tak jak wcześniej napisano, CBQ dba cały czas o to, by łącze było przez odpowiedni czas bezczynne i dzięki temu prawdziwa przepustowość spadła do skonfigurowanej wielkości. Żeby to robić kalkuluje czas, który powinien upłynąć pomiędzy kolejnymi pakietami. W czasie pracy, efektywny czas bezczynności mierzony jest za pomocą `wykładnika ważonego średniego przesyłu' (ang. "exponential weighted moving average", EWMA), który traktuje świeże pakiety jako wykładniczo ważniejsze niż starsze. Tak samo kalkulowany jest poziom obciążenia stacji uniksowych. Wyliczony czas bezczyności odejmowany jest od wartości wyliczonej przez EWMA i wynik nazywa się średnim czasem bezczynności - `avgidle'. Dokładnie załadowane łącze ma ten czas równy zero: pakiety docierają dokładnie co jeden wyliczony interwał czasowy. Przeładowane łącze ma ujemny średni czas bezczynności, a jeśli wartość ta jest duża, CBQ zamyka je na chwilę i oznacza to jako `przekroczenie limitu' (ang. "overlimit"). Odpowiednio, łącze puste ma duży średni czas bezczynności, który po paru godzinach ciszy umożliwiłby zajęcie nieskończonej przepustowości. By temu zapobiec, wartość średniego czasu bezczynności ogranicza się odgórnie parametrem `maxidle'. Jeśli dojdzie do przekroczenia limitu, CBQ zdławi się na dokładnie tyle czasu ile zostało wyliczone pomiędzy pakietami a następnie przepuści jeden pakiet i zdławi się znowu. Sprawdź znaczenie parametru `minburst'. Poniższe parametry można podać by skonfigurować kształtowanie ruchu: avpkt Średnia wielkość pakietu, mierzona w bajtach. Potrzebna do wyli