11. Filtr pakietów sieciowych (/etc/config/firewall)
Router posiada wbudowany firewall broniący bezpieczeństwa naszej sieci domowej. Zapora ta realizuje z grubsza trzy rzeczy. Pierwszą z nich jest udostępnianie połączenia sieciowego wielu maszynom wewnątrz sieci domowej. Drugim jej celem jest przepuszczanie ruchu w obu kierunkach do określonych hostów i na określony porty. No i ostatnia sprawa to forwardowanie połączeń pochodzących z internetu, do określonych hostów lokalnych. Wszystkie te trzy zadania realizowane są przez iptables.
System OpenWRT posiada domyślnie skonfigurowany firewall, który przepuszcza wszystko z sieci do internetu, natomiast niekoniecznie to działa tak samo w drugą stronę. Jeśli posiadamy routowalny (zewnętrzny) adres IP, jesteśmy widoczni w internecie i każdy, kto zechce, może się z nami połączyć i tu właśnie do gry wchodzi iptables uniemożliwiając temu komuś na takie zachowanie.
Wszelkie reguły dotyczące zachowania się zapory definiujemy w pliku /etc/config/firewall . Ja orientuję się trochę w samym filtrze iptables, a mimo to miałem (i dalej) mam problemy z ogarnięciem reguł stworzony przez skrypty OpenWRT. Dlatego też znalazłem się w sytuacji, w której musiałem dokonać wyboru -- albo poświęcę mnóstwo czasu na analizę i interpretację tego co wyrzuca polecenie iptables -nvL (dla każdej z 4 tablic), albo zwyczajnie zaimplementować sobie własny filtr, który stworzę od 0. Na to drugie rozwiązanie się zdecydowałem i nie będę tutaj opisywał pliku /etc/config/firewall . Zamiast tego, opiszę procedurę stworzenia własnego filtra, oraz po trochu jego zasadę działania
11.1 Własny filtr iptables
Jak już zapewne zdążyliśmy wyczytać, potrzebny będzie nam dodatkowy skrypt startowy, który załaduje nasz filtr podczas startu routera. Tworzymy zatem pusty plik, do którego będziemy dodawać poniżej opisane reguły. Skryptowi musimy nadać także prawa wykonywania:
touch /etc/init.d/fw
chmod +x /etc/init.d/fw
Edytujemy teraz ten plik i wklejamy do niego standardową zawartość skryptu startowego:
#!/bin/sh /etc/rc.common
START=17
start() {
}
stop() {
}
Kluczową sprawą jest dobór wartości parametru START -- musimy tak ustawić naszą zaporę by załadowała się przed poniesieniem sieci (skrypt /etc/init.d/network). Listując katalog /etc/rc.d/ , możemy zaobserwować, że skrypt od sieci ma numerek 20, zatem nagłówek naszego pliku musi mieć numer niższy, w tym wypadku ustawiliśmy 17. Bloki start() oraz stop() będą wykonywane przy odpalaniu skryptu z opcją start/stop.
Iptables składa się z kilku tablic. Do naszej dyspozycji są: raw, magnle, nat i filter. Ponadto, każda z tablic ma pewien zestaw łańcuchów, w których skład mogą wchodzić: INPUT, OUTPUT, FORWARD, PREROUTING oraz POSTROUTING. Pakiety przychodzące, wychodzące i przechodzące przez router, robią to w ściśle określonej kolejności, którą obrazuje poniższa fotka:
To, którym pakietom zezwolimy na wejście oraz wyjście do/z routera, będziemy określać w tablicy filter, w łańcuchach INPUT oraz OUTPUT. Wszystko co będzie przechodzić przez router, będziemy regulować w łańcuchu FORWARD, też w tej tablicy. Natomiast wszystko to, co związane jest z translacją adresów, będziemy wpisywać do tablicy nat, odpowiednio do łańcuchów PREROUTING/POSTROUTING.
Na każdej maszynie, która ma dostęp do internetu, firewall buduje się w ten sam sposób -- najpierw blokujemy cały ruch pochodzący z zewnątrz sieci do, w tym przypadku, routera. Dodatkowo, blokujemy pakiety przechodzące przez router, czyli te, których przeznaczeniem nie jest ta konkretna maszyna. Z kolei pakiety generowane przez router powinny bez problemu mieć wyjście na świat, także nie ma potrzeby ustawiać domyślnej polityki dla pakietów wychodzących, chyba, że projektujemy jakiś wymyślny serwer, który będzie nadawał tylko na określonych portach, lub mamy niezaufaną sieć domową. Tak czy inaczej, my skupimy się na zbudowaniu zapory, która zabezpieczy dostęp do sieci domowej z zewnątrz.
Ustawiamy domyślną politykę dla łańcuchów:
iptables -t filter -F
iptables -t filter -X
iptables -t filter -P INPUT DROP
iptables -t filter -P FORWARD DROP
iptables -t filter -P OUTPUT ACCEPT
Oczywiście, powyższe linijki wpisujemy do pliku, a nie do terminala, bo to by zaowocowało zerwaniem połączenia.
Zanim nauczymy nasz router forwardować zapytania, musimy go pierw nauczyć jak przetwarzać pakiety, które w ogóle do niego docierają, a te mogą trafić do niego mając określone stany połączenia: NEW, ESTABLISHED i RELATED. Stan NEW identyfikuje pakiety rozpoczynające nowe połączenie, stan ESTABLISHED identyfikuje te pakiety, które należą już do istniejących połączeń, z kolei stan RELATED określa pakiety, które nie są bezpośrednio powiązane już z istniejącymi połączeniami, np. przy protokole FTP. Cała magia dotycząca śledzenia połączeń odbywa się w pliku /proc/net/nf_conntrack i to na podstawie tych wpisów kernel wie, które pakiety odnoszą się do konkretnych połączeń.
Każdy pakiet TCP ma dodatkowo ustawione flagi, które określają dokładnie co to jest za rodzaj pakietu, np. czy ma on na celu otwarcie nowego połączenia czy też np. jest to potwierdzenie przesłania pewnej ilości danych z jednego hosta do drugiego. Są pewne określone sekwencje flag, które umożliwiają komunikację sieciową, niemniej jednak, można tymi flagami dowolnie manipulować i takimi wykutymi pakietami można dokonywać skanowania portów w poszukiwaniu usług sieciowych, np. SSH, co zwykle może się skończyć atakiem, w przypadku gdy określona usługa zostanie przez atakującego wykryta. Na szczęście istnieje jeden pseudo stan pakietu -- INVALID , w którym to są zdefiniowane wszystkie nieprawidłowe kombinacje flag. Dzięki takiej opcji, mamy możliwość odfiltrować tego typu pakiety. Niemniej jednak, stan INVALID nie zabezpieczy nas całkowicie przed skanami portów.
Reasumując, musimy zezwolić na przetwarzanie pakietów w stanie ESTABLISHED oraz RELATED , zrzucać pakiety w stanie INVALID i manipulować pakietami w stanie NEW . Zatem dopisujemy poniższe linijki do pliku zapory:
iptables -t filter -N tcp
iptables -t filter -N udp
iptables -t filter -N icmp_in
iptables -t filter -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -t filter -A INPUT -m conntrack --ctstate INVALID -j DROP
iptables -t filter -A INPUT -i lo -j ACCEPT
iptables -t filter -A INPUT -p icmp -m conntrack --ctstate NEW -j icmp_in
iptables -t filter -A INPUT -p udp -m conntrack --ctstate NEW -j udp
iptables -t filter -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j tcp
iptables -t filter -A INPUT -p tcp -j REJECT --reject-with tcp-reset
iptables -t filter -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
iptables -t filter -A INPUT -j REJECT --reject-with icmp-proto-unreachable
Powyżej została też uwzględniona pętla zwrotna (to ta linijka z "-i lo"), której komunikację trzeba przepuścić -- są to pakiety lokalne, które nie wychodzą poza router. Jeśli jakaś usługa nasłuchuje na 127.0.0.0/8 , żaden inny host, za wyjątkiem localhosta, nie może z nią nawiązać połączenia. W przypadku gdybyśmy zablokowali ten ruch, pewne procesy systemowe mogą przestać działać.
Stworzyliśmy także sobie trzy nowe łańcuchy: tcp, udp i icmp_in -- po jednym dla tych ważniejszych protokołów i odpowiednio przekierowaliśmy do nich ruch.
Ostatnie trzy reguły zajmują się zwracaniem odpowiednich komunikatów hostom, które próbowały się połączyć z jakąś usługą nasłuchującą na naszym routerze ale jej nie zastały -- być może była wyłączona, być może nigdy nie istniała i dostaliśmy zwykły skan portu.
Zwykle routery mają kilka interfejsów sieciowych, w tym przypadku mam do dyspozycji jeden WAN (eth0) oraz jeden LAN (br-lan). Usługi dla tych interfejsów trzeba konfigurować osobno, tj. jeśli jakaś usługa, np. SSH ma być widoczna w internecie, trzeba uwzględnić w konfiguracji iptables -i eth0 , natomiast jeśli będziemy się łączyć do shella tylko lokalnie, wtedy precyzujemy opcję -i br-lan. Z kolei, jeśli jakaś usługa ma być dostępna zarówno w internecie jak i na lanie, ten parametr możemy pominąć.
Jako, że w założeniu mamy iż sieć domowa jest zaufana, zezwolimy wszystkim jej maszynom na dostęp do routera do wszystkich usług, w tym też i do SSH:
iptables -t filter -A icmp_in -p icmp -i br-lan -s 192.168.1.0/24 -j ACCEPT
iptables -t filter -A tcp -p tcp -i br-lan -s 192.168.1.0/24 -j ACCEPT
iptables -t filter -A udp -p udp -i br-lan -s 192.168.1.0/24 -j ACCEPT
Przy czym, trzeba zauważyć, że powyższe reguły przepuszczają pakiety w stanie NEW. Pozostała komunikacja z routerem odbywa się za pomocą wcześniej sprecyzowanej reguły, czyli -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT . Pakiety w stanie NEW pochodzące z innego interfejsu niż br-lan w dalszym ciągu są blokowane, a bez nich nie da rady otworzyć połączenia, tak by pakiety w stanach RELATED i ESTABLISHED mogły zostać przesłane.
Jeśli byśmy chcieli zezwolić na ping routera lub też jeśli chcielibyśmy mieć możliwość logowania się na shella spoza sieci domowej, możemy dopisać poniższe linijki:
iptables -t filter -A icmp_in -p icmp -i eth0 -j ACCEPT
iptables -t filter -A tcp -p tcp -i eth0 -m tcp --dport 22 -j ACCEPT
Gdybyśmy wystartowali teraz nasz skrypt, mielibyśmy dostęp tylko do routera. Nie działa jeszcze forwardowanie pakietów -- zarówno od/do tych maszyn znajdujących się za routerem, jak i również między dwoma hostami samej sieci domowej.
By połączyć się z internetem z maszyny znajdującej się za routerem, musimy nakazać naszemu routerowi, by w miejsce adresu w nagłówku pakietu wpisywał swój adres jako adres źródłowy ale tylko w przypadku gdy pakiety nie są adresowane bezpośrednio do niego. W tym celu możemy użyć, albo -j SNAT , albo -j MASQUERADE . Pierwszy z nich powinien być używany w przypadku jeśli router ma stały adres IP, drugi zaś jeśli to IP się zmienia. Choć można używać także MASQUERADE ze stałym IP. Poniżej przykład zastosowania obu z nich, musimy wybrać jeden, w zależności od konfiguracji routera:
iptables -t nat -A POSTROUTING -o eth0 -s 192.168.1.0/24 -j SNAT --to-source 22.33.44.55
iptables -t nat -A POSTROUTING -o eth0 -s 192.168.1.0/24 -j MASQUERADE
Kluczowe w ustawianiu translacji adresów jest określenie interfejsu, przez który pakiety mają wychodzić do internetu, w tym przypadku jest to interfejs WAN (eth0). Jeśli korzystamy z -j SNAT, ustawiamy zewnętrzny adres, który został przypisany interfejsowi eth0.
By móc się połączyć ze światem oraz z innymi komputerami w sieci, musimy jeszcze dodać odpowiednie wpisy w łańcuchu FORWARD, w tablicy filter w pliku zapory:
iptables -t filter -N fw-interfaces
iptables -t filter -N fw-open
iptables -t filter -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -t filter -A FORWARD -m conntrack --ctstate INVALID -j DROP
iptables -t filter -A FORWARD -m conntrack --ctstate NEW -j fw-interfaces
iptables -t filter -A FORWARD -m conntrack --ctstate NEW -j fw-open
iptables -t filter -A FORWARD -j REJECT --reject-with icmp-host-unreachable
Podobnie jak w przypadku łańcuchu INPUT, pozwalamy przechodzić pakietom w stanie RELATED i ESTABLISHED, zrzucamy pakiety w stanie INVALID i manipulujemy pakietami w stanie NEW. Ten ruch w stanie NEW, przechodzi odpowiednio dwa inne łańcuchy: fw-interfaces oraz fw-open . W pierwszym z nich są definiowane interfejsy routera, między którymi pakiety mogą przechodzić, np. z br-lan do eth0 lub, co zapewni łączność z internetem oraz komunikację między hostami w sieci domowej. W drugim łańcuchu zaś są definiowane regułki przekierowujące ruch do określonych maszyn za routerem.
Dodajemy do łańcucha fw-interfaces w pliku firewalla poniższą linijkę:
iptables -t filter -A fw-interfaces -i br-lan -s 192.168.1.0/24 -j ACCEPT
Teraz już powinien działać zarówno internet jak i komunikacja pomiędzy hostami w sieci. By zweryfikować czy aby faktycznie wszystko działa jak należy, przy pomocy polecenia ping odpytujemy jakiś host w internecie oraz host w naszej sieci. Dobrze jest to robić po adresie IP, by wyeliminować ewentualne problemy związane z rozwiązywaniem nazw (DNS).
Jeśli będziemy chcieli przekierować ruch z internetu na maszynę za routerem, możemy to zrobić posługując się -j DNAT. W tym celu do pliku firewalla dodajemy poniższe wpisy:
iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 22222 -j DNAT --to-destination 192.168.1.150:22222
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 22222 -j DNAT --to-destination 192.168.1.150:22222
iptables -t filter -A fw-open -i eth0 -o br-lan -d 192.168.1.150 -p tcp --dport 22222 -j ACCEPT
iptables -t filter -A fw-open -i eth0 -o br-lan -d 192.168.1.150 -p udp --dport 22222 -j ACCEPT
Adres określony w --to-destination , to oczywiście adres maszyny, do której mają trafić pakiety po przekierowaniu. Z kolei, port widniejący po dwukropku, to port na hoście docelowym, na którym nasłuchuje jakaś usługa. W pierwszych dwóch linijkach mamy także opcje --dport 22222 określającą na jaki port w routerze będą przychodzić pakiety z internetu, które trzeba będzie przesłać do maszyny za routerem. Z kolei dwie ostatnie linijki umożliwiają przejście tym pakietom z interfejsu eth0 na br-lan .
Porty w --dport i --to-destination w pierwszych dwóch linijkach nie koniecznie muszą być takie same. Natomiast wartość określona w --to-destination musi być taka sama co w --dport w dwóch ostatnich linijkach. W przypadku braku zdefiniowania portu po dwukropku (po adresie) w pierwszych dwóch linijkach, zostanie ustawiony taki sam port jak w opcji --dport .
Konfiguracja maszyny otrzymywana od ISP zwykle jest przesyłana za pomocą protokołu DHCP, musimy zatem też zezwolić na komunikację z upstreamowym serwerem DHCP, jeśli nie znamy jego adresu IP, to wystarczy dopisać poniższe linijki:
iptables -t filter -A udp -p udp --sport 68 --dport 67 -s 0.0.0.0 -d 255.255.255.255 -j ACCEPT -m comment --comment "DHCP-broadcast"
iptables -t filter -A udp -p udp --dport 68 -j ACCEPT -m comment --comment "Allow-DHCP-Renew"
Jeśli znamy adres serwera DHCP, możemy dodatkowo posłużyć się parametrem -s i uwzględnić w tych regułach adres źródłowy, co dodatkowo zwiększy bezpieczeństwo sieci.
Reasumując powyższe informacje, potrzebny nam skrypt powinien się prezentować mniej więcej tak:
#!/bin/sh /etc/rc.common
START=17
start() {
iptables -t filter -F
iptables -t filter -X
iptables -t filter -P INPUT DROP
iptables -t filter -P FORWARD DROP
iptables -t filter -P OUTPUT ACCEPT
iptables -t filter -N tcp
iptables -t filter -N udp
iptables -t filter -N icmp_in
iptables -t filter -N fw-interfaces
iptables -t filter -N fw-open
iptables -t filter -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -t filter -A INPUT -m conntrack --ctstate INVALID -j DROP
iptables -t filter -A INPUT -i lo -j ACCEPT
iptables -t filter -A INPUT -p icmp -m conntrack --ctstate NEW -j icmp_in
iptables -t filter -A INPUT -p udp -m conntrack --ctstate NEW -j udp
iptables -t filter -A INPUT -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j tcp
iptables -t filter -A INPUT -p tcp -j REJECT --reject-with tcp-reset
iptables -t filter -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
iptables -t filter -A INPUT -j REJECT --reject-with icmp-proto-unreachable
iptables -t filter -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -t filter -A FORWARD -m conntrack --ctstate INVALID -j DROP
iptables -t filter -A FORWARD -m conntrack --ctstate NEW -j fw-interfaces
iptables -t filter -A FORWARD -m conntrack --ctstate NEW -j fw-open
iptables -t filter -A FORWARD -j REJECT --reject-with icmp-host-unreachable
iptables -t filter -A icmp_in -p icmp -i br-lan -s 192.168.1.0/24 -j ACCEPT
iptables -t filter -A tcp -p tcp -i br-lan -s 192.168.1.0/24 -j ACCEPT
iptables -t filter -A udp -p udp -i br-lan -s 192.168.1.0/24 -j ACCEPT
iptables -t filter -A udp -p udp --sport 68 --dport 67 -s 0.0.0.0 -d 255.255.255.255 -j ACCEPT -m comment --comment "DHCP-broadcast"
iptables -t filter -A udp -p udp --dport 68 -j ACCEPT -m comment --comment "Allow-DHCP-Renew"
iptables -t filter -A fw-interfaces -i br-lan -s 192.168.1.0/24 -j ACCEPT
# iptables -t filter -A fw-open -i eth0 -o br-lan -d 192.168.1.150 -p tcp --dport 22222 -j ACCEPT
# iptables -t filter -A fw-open -i eth0 -o br-lan -d 192.168.1.150 -p udp --dport 22222 -j ACCEPT
# iptables -t filter -A icmp_in -p icmp -i eth0 -j ACCEPT
# iptables -t filter -A tcp -p tcp -i eth0 -m tcp --dport 22 -j ACCEPT
iptables -t nat -A POSTROUTING -o eth0 -s 192.168.1.0/24 -j SNAT --to-source 22.33.44.55
# iptables -t nat -A POSTROUTING -o eth0 -s 192.168.1.0/24 -j MASQUERADE
# iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 22222 -j DNAT --to-destination 192.168.1.150
# iptables -t nat -A PREROUTING -i eth0 -p udp --dport 22222 -j DNAT --to-destination 192.168.1.150
}
stop() {
}
Znak # na początku linii oznacza komentarz, czyli, że dana reguła nie zostanie dodawana do filtra przy wywołaniu skryptu -- dostosować według potrzeb.
Blok start() mamy z głowy, teraz jeszcze przydałoby się coś naskrobać w bloku stop(). Musimy tak zaprojektować iptables, by przy wywoływaniu skryptu z opcją stop, komunikacja została przerwana ale bez tracenia bezpośredniego połączenia z routerem. Nic trudnego, wystarczy usunąć wszystkie niestandardowe reguły, a te podstawowe przepisać na interfejs br-lan , przykładowo:
stop() {
for TABLE in \
"-t raw" \
"-t mangle" \
"-t filter" \
"-t nat"
do
iptables $TABLE -F
iptables $TABLE -X
done
iptables -t filter -N tcp
iptables -t filter -N udp
iptables -t filter -P INPUT DROP
iptables -t filter -P FORWARD DROP
iptables -t filter -A INPUT -i br-lan -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -t filter -A INPUT -i lo -j ACCEPT
iptables -t filter -A INPUT -i br-lan -p icmp -m conntrack --ctstate NEW -j ACCEPT
iptables -t filter -A INPUT -i br-lan -p udp -m conntrack --ctstate NEW -j ACCEPT
iptables -t filter -A INPUT -i br-lan -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m conntrack --ctstate NEW -j ACCEPT
iptables -t filter -A INPUT -p tcp -j REJECT --reject-with tcp-reset
iptables -t filter -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable
iptables -t filter -A INPUT -j REJECT --reject-with icmp-proto-unreachable
}
I w taki sposób mamy dwa bloki potrzebne skryptowi startowemu do prawidłowego działania. Z tym, że OpenWRT ma swój domyślny filtr pakietów w /etc/init.d/firewall i trzeba ten skrypt wyłączyć z autostartu routera, w przeciwnym razie, nasze ustawienia mogą zostać nadpisane. Skrypt wyłączamy przez:
# /etc/init.d/firewall disable
Dodajemy teraz nasz skrypt, który będzie ustawiał regułki za każdym razem gdy router będzie się budził do życia:
# /etc/init.d/fw enable
Jeśli wszystko przeprowadziliśmy zgodnie z powyższym opisem, router powinien się załadować po zresetowaniu. Komunikacja między hostami w sieci powinna działać, tak samo jak i internet powinien.
11.2 Ustawianie odpowiedniego TTL dla pakietów
Czasem niektórzy ISP z jakiegoś bliżej nieokreślonego powodu blokują dostęp do internetu hostom zlokalizowanym za routerem. Z grubsza, ISP blokuje, albo konkretny TTL pochodzący od hostów w sieci, albo ogranicza TTL do jednego hopa wszystkim pakietom dochodzącym do routera z jego strony. Jeśli mamy nieszczęście trafić na takiego ISP, możemy przy pomocy iptables ustawić/podbić/zmniejszyć TTL pakietów, które docierają do routera zarówno od strony LAN jak i WAN i tym samym bez większego trudu sobie poradzić tą blokadą.
Każdy system operacyjny ma skonfigurowaną domyślną wartość TTL dla wszystkich pakietów tworzonych na obsługiwanym przez ten system maszynie. Dla windowsów jest to 128, a dla linuxów, w tym też i OpenWRT, jest to 64. Tą wartość możemy oczywiście zweryfikować wydając na routerze poniższe polecenie:
# sysctl -a | grep ttl
net.ipv4.ip_default_ttl = 64
Niemniej jednak, powyższe ustawienie dotyczy wyłącznie pakietów tworzonych na routerze i nie ma wpływu na pakiety przechodzące przez niego z/do sieci/internetu.
W przypadku forwardowania pakietów, routery zmniejszają wartość TTL o 1, czyli otrzymując pakiet od maszyny linuxowej, po przetworzeniu go, będzie on wysłany do następnego routera czy hosta z TTL=63. W przypadku ISP obniżających TTL do 1, router otrzyma pakiet z TTL=1, po czym obniży wartość tego pola do 0 i jeśli pakiet nie jest przeznaczony dla routera, ten musi go zniszczyć.
By poradzić sobie z tą niedogodnością, musimy doinstalować dodatkowy moduł iptables:
# opkg update
# opkg install iptables-mod-ipopt kmod-ipt-ipopt
Edytujemy teraz wcześniej stworzony skrypt firewalla i dodajemy do niego poniższe linijki w bloku start() :
iptables -t mangle -I PREROUTING -i eth0 -m ttl --ttl-lt 2 -j TTL --ttl-inc 1
iptables -t mangle -I PREROUTING -i br-lan -m ttl --ttl-eq 64 -j TTL --ttl-inc 1
iptables -t mangle -I PREROUTING -i br-lan -m ttl --ttl-eq 128 -j TTL --ttl-inc 1
Pierwsza linijka sprawdza wszystkie pakiety przychodzące z internetu (interfejs eth0) i próbuje je dopasować w oparciu o pole TTL w nagłówku IP. Jeśli TTL jest ustawiony na mniejszy od 2 (-m ttl --ttl-lt 2), to pole zostanie przepisane i zwiększony zostanie numerek o 1 (-j TTL --ttl-inc 1).
Pozostałe dwie linijki sprawdzają pakiety pochodzące z sieci lokalnej (interfejs br-lan) pod kątem standardowych TTL dla systemów operacyjnych (-m ttl --ttl-eq 64, dla linuxa oraz -m ttl --ttl-eq 128 dla windowsa) i podobnie jak wyżej, w przypadku dopasowania, TTL jest zwiększany o 1.
Potrzebujemy, albo pierwszej, albo dwóch ostatnich linijek i to, która znajdzie zastosowanie w naszym wypadku możemy sprawdzić przez dodanie pierwszej z nich do iptables -- jeśli pakiety będą uderzać w tą regułę, znaczy, że ISP obniżył TTL do wartości 1. W przeciwnym wypadku, musimy skorzystać z pozostałych wpisów.