Hlavní navigace

Policy-based routing v Linuxu: směrování provozu pod kontrolou

11. 1. 2022
Doba čtení: 7 minut

Sdílet

 Autor: Depositphotos
Tradiční směrování síťových paketů je založeno na cílové adrese. Pokud ale chceme více, potřebujeme policy-based routing (PBR). To nám umožní směrovat provoz podle mnoha dalších kritérií.

Tradiční přístup

Při zpracování síťového provozu nás zajímá především směrování jednotlivých paketů. Počítače v síti se musejí zabývat tím, kterým rozhraním má konkrétní paket odejít. Koncový uzel to řeší pro svůj vlastní odchozí provoz, směrovač (router) pak zpracovává i cizí provoz. Každý paket musí být prozkoumán a musí být rozhodnuto, kam půjde dál.

V tradičním schématu hraje zásadní roli takzvaná směrovací tabulka. Ta obsahuje informace o cílových síťových rozsazích spolu s rozhraními, přes které jsou tyto rozsahy dostupné. Nejjednodušší tabulka u koncového stroje může vypadat například takto:

# ip route
default via 192.168.1.1 dev enp0s3
192.168.1.0/24 dev enp0s3 proto kernel scope link src 192.168.1.184

V tabulce jsou dva záznamy: jeden pro místní síť 192.168.1.0/24 a druhý pro celý internet, tedy 0.0.0.0/0, zde zastoupeno slovem default (výchozí). Řádků bude více, pokud budeme mít více síťových rozhraní nebo pro nás bude dostupných více síťových rozsahů. Typický uživatel se s tím setká v souvislosti s VPN. Nahozením vznikne nové virtuální síťové rozhraní, získá nějaké IP adresy a přibude tím také záznam do směrovací tabulky.

Rozhodování o směrování probíhá výhradně podle cílové IP adresy. Jádro operačního systému se podívá na hlavičku paketu a porovná cílovou adresu s obsahem tabulky. Najde tam největší možnou shodu (nejdelší prefix shodný) a to bude cíl našeho směrování. Jinými slovy: pokud paket patří do naší sítě, bude tam odeslán, jinak přijde na řadu nejobecnější výchozí prefix a provoz míří na bránu. Tam proběhne stejný mechanismus rozhodování.

Když chceme víc

Tohle je známý přístup, který vyhovuje ve většině běžných situací. Má ale zřejmé omezení vycházející z toho, že umožňuje porovnávat jen cílovou adresu. Provoz mířící do jednoho cíle bude vždy směrován stejnou cestou. Nemáme možnost nadefinovat vlastní pravidla, která by například zohledňovala různé vlastnosti zdroje komunikace. Pro podobnou funkcionalitu musíme sáhnout po policy-based routingu (PBR), což bychom česky mohli přeložit jako směrování založené na pravidlech.

Na omezení základní směrovací tabulky narazíme ve chvíli, kdy máme více odchozích linek, které mohou odbavit stejný provoz. Můžeme například používat více různých poskytovatelů nebo máme různé části sítě propojené třeba pomocí tunelů. Můžeme mít také různé druhy provozu, které chceme posílat různými směry. Například máme jednu linku vyhrazenou jen pro správu (management) a nechceme si ji ucpávat běžným uživatelským provozem.

Více směrovacích tabulek

Směrovač v linuxovém jádře může být doplněn o pravidla, která cíleně posílají pakety různými směry. Je to implementováno pomocí více směrovacích tabulek, které samy o sobě fungují úplně běžným popsaným způsobem. Nad těmito tabulkami ale stojí seznam pravidel, která rozhodují o tom, podle které tabulky bude ten či onen paket dále posuzován.

Ve výchozím stavu už máte v systému tři směrovací tabulky: local, main a default. Tabulku local si udržuje jádro samo a obsahuje prioritní cesty pro místní adresy a broadcastové směrování. Tabulka main je ta, se kterou pracujeme, pokud neurčíme jinak. Poslední tabulka s názvem default je prázdná a je vyhrazena pro zpracování paketů, které nebyly vyřízeny v hlavní tabulce.

Seznam pravidel (a tedy tabulek) si můžeme snadno zobrazit:

# ip rule
0:  from all lookup local
32766:  from all lookup main
32767:  from all lookup default

Číslo na začátku každého řádku značí prioritu. Pravidla jsou zkoumána podle priority vzestupně – nejprve se tedy správný prefix hledá v lokální tabulce, pak v hlavní a nakonec v té poslední určené pro případné další akce.

Jak už to v systémech unixového typu bývá u většiny objektů, jsou i směrovací tabulky pojmenovány pomocí unikátního číselného identifikátoru. Ten je na jádrech 2.6 a novějších dvaatřicetibitový (integer), u starších jader byl osmibitový. Teprve až pro uživatelský výstup je číselný identifikátor nahrazen slovním označením (jménem). Tato vazba je uložena v jednoduchém textovém souboru v  /etc/iproute2/rt_tables.

Tabulky není potřeba explicitně zakládat. Chová se to tak, jako by všechny tabulky automaticky existovaly, jen je máme prázdné a neodkazujeme na ně v pravidlech. Pokud tedy rovnou přidáme pravidlo odkazující na tabulku nebo do nové tabulky přidáme prefix, podaří se to. Jako referenci můžeme použít číselný identifikátor nebo jméno, které předem vložíme do zmíněné databáze tabulek – obě označení jsou rovnocenná.

# echo "100 moje" >> /etc/iproute2/rt_tables

Od této chvíle můžeme pro tabulku používat označení slovem, takže se nám s ní bude lépe pracovat. Naplníme ji standardním způsobem pomocí ip route, přičemž přidáme ještě klíčové slovo table, abychom mohli uvést, do které tabulky chceme daný záznam přidat.

# ip route add default via 192.168.2.1 dev enp1s3 table moje
# ip route add 192.168.2.0/24 dev enp1s3 table moje
…

Obsah tabulky si pak vypíšeme jednoduše tak, že opět přidáme informaci o tom, co přesně chceme vidět.

# ip route list table moje

Pravidlo pro výběr tabulky

Máme sice další tabulku, ale zatím ji nepoužíváme. Je čas přidat pravidlo pro policy-base routing, aby za určitých okolností byla vybrána tato naše nová tabulka. Klasickým způsobem použití je výběr podle zdrojové adresy. Řekněme, že provoz z jedné adresy chceme směrovat podle naší nové tabulky.

# ip rule add from 192.168.2.123 lookup moje

Přidali jsme nové pravidlo, které se týká jen jedné adresy. Objeví se mezi dalšími pravidly a na první pohled je jasné, co toto pravidlo dělá:

# ip rule
0:  from all lookup local
32765:  from 192.168.2.123 lookup moje
32766:  from all lookup main
32767:  from all lookup default

Pokud bychom chtěli pravidlu přidělit explicitní prioritu, přidali bychom při jeho zavedení volbu pref  následovanou konkrétní číselnou hodnotou.

Použili jsme volbu from, která umožňuje vybrat pakety podle zdrojové adresy. Linuxový policy routing umožňuje vybírat pakety podle mnoha dalších kritérií. Všechny možnosti shrnuje následující tabulka:

Volba Vysvětlení
from podle zdrojového prefixu
to podle cílového prefixu
iif podle příchozího rozhraní
oif podle odchozího rozhraní
tos | dsfeld podle Type Of Service (TOS)
fwmark podle značky fwmark
uidrange podle uživatelského ID
ipproto podle protokolu za IP hlavičkou
sport podle zdrojového portu (rozsahu)
dport podle cílového portu
protocol podle směrovacího protokolu

Pozor: Ne všechny volby jsou dostupné ve všech případech, například volba from nebude fungovat pro originující provoz, protože v době prohledávání směrovací tabulky ještě není místní IP adresa známa – ta je určena až po rozhodnutí, kterým směrem datagram odejde.

Příklad: jak to dělá WireGuard

O nástroji WireGuard máme na Rootu celý seriál, jde o novou linuxovou VPN, která vyniká svou přímočarostí a jednoduchostí. WireGuard využívá policy routing, pokud jej použijeme v režimu výchozí brány. V tom případě totiž musí směrovač v jádře rozlišit, který provoz má být směrován do virtuálního rozhraní WireGuardu a co už jím prošlo a je třeba to odeslat do internetu.

Využívá k tomu strukturu fwmark, která umožňuje sledovat a označkovat provoz tekoucí síťovým subsystémem. Takto označený provoz pak může posílat jinou cestou. Při aktivaci VPN pomocí skriptu wg-quick proběhnou konkrétně tyto příkazy:

# wg set wg0 fwmark 51820
# ip route add default dev wg0 table 51820
# ip rule add not fwmark 51820 table 51820
# ip rule add table main suppress_prefixlength 0

Nejprve je uvnitř WireGuardu zapnuto, že se mají odchozí pakety označkovat pomocí hodnoty 51820. Poté už přichází klasická práce s policy routingem. Nejprve je přidána výchozí brána do směrovací tabulky číslo 51820 a poté jsou zadána dvě pravidla: neoznačkovaný provoz pošli do této nové tabulky, prohledej tabulku main a ignoruj přitom výchozí bránu.

Výsledná pravidla vypadají takto:

# ip rule
0:      from all lookup local
32764:  from all lookup main suppress_prefixlength 0
32765:  not from all fwmark 0xca6c lookup 51820
32766:  from all lookup main

Nejprve je prohledána tabulka s lokálními adresami našeho stroje, poté je prohlédána tabulka main a ignoruje se výchozí brána. Tento krok odbaví provoz do naší místní sítě. Poté se správným fwmarkem neoznačkovaný provoz pošle do tabulky 51820, ve které je jen výchozí brána do zařízení wg0. Pokud už byl provoz zpracován a označkován, skončí v posledním pravidle, které jej pošle opět do tabulky main. Poté odchází jako běžný provoz do standardní sítě a zamíří výchozí bránou do světa.

Hacking tip

Výhodou tohoto přístupu je, že nijak nezasahuje do tabulky main, takže není potřeba v ní dělat žádnou úpravu výchozí brány, kterou by DHCP klient zase zrušil. Druhým řešením (které používá OpenVPN) je přidání dvou specifičtějších cest, což sice zachová původní výchozí bránu, ale shození a nahození fyzického rozhraní výchozí bránu odebere a provoz se začne pomocí specifičtějších pravidel točit uvnitř VPN.

WireGuard proto používá policy routing, aby svá pravidla postavil mimo hlavní směrovací tabulku. Elegantně se tak vyhnul zásahům do pravidel, která systém pomocí DHCP obdržel od místní sítě.

Odkazy

Autor článku

Petr Krčmář pracuje jako šéfredaktor serveru Root.cz. Studoval počítače a média, takže je rozpolcen mezi dva obory. Snaží se dělat obojí, jak nejlépe umí.