Nastavení síťového stacku v Linuxu

Statický IP routing

IP routing probihá v principu na každém uzlu IP sítě, minimálně proto, aby se uzel rozhodl, jaký traffic doručí přes connected route a co pošlet přes statické záznamy ve směrovací tabulce, například přes default route. K tomu není zapotřebí nic extra, prostě se nastaví správné záznamy do směrovací tabulky. Chceme-li však, aby Linux skutečně routoval, tedy že když dostane IP datagram na jednom rozhraní, provede vyhledání cílové adresy datagramu ve směrovací tabulce a najde-li next-hop, tak odešle datagram na MAC adresu next-hopu přes jiné síťové rozhraní, musí mít nastavené sysctl flagy: net.ipv4.conf.all.forwarding = 1 pro IPv4 a net.ipv6.conf.all.forwarding = 1, případně lze tento flag nastavit i pro jednotlivá rozhraní.

Předpokládejme, že máme nastavenou VLAN 10 a dummy interface z předchozích sekcí. Nyní chceme statickým routingem zařídit, abychom z c1 mohli komunikovat na adresu na dummy0 rozhraní na c2 a obráceně. To bez routingu nejde:

root@c1:/# ping 10.254.2.1
PING 10.254.2.1 (10.254.2.1) 56(84) bytes of data.
^C
--- 10.254.2.1 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 3ms

Začneme tím, že ověříme, zda máme adresy na rozhraních, jak jsme očekávali na kontejneru c1:

root@c1:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0@if4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:73:04:49 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.3.9/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::216:3eff:fe73:449/64 scope link 
       valid_lft forever preferred_lft forever
3: eth0.10@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:73:04:49 brd ff:ff:ff:ff:ff:ff
    inet 172.16.1.1/24 scope global eth0.10
       valid_lft forever preferred_lft forever
    inet6 2001:db8::1/64 scope global 
       valid_lft forever preferred_lft forever
    inet6 fe80::216:3eff:fe73:449/64 scope link 
       valid_lft forever preferred_lft forever
7: dummy0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether 32:98:14:e9:f2:94 brd ff:ff:ff:ff:ff:ff
    inet 10.254.1.1/32 scope global dummy0
       valid_lft forever preferred_lft forever
    inet6 fe80::3098:14ff:fee9:f294/64 scope link 
       valid_lft forever preferred_lft forever

A taktéž na c2:

root@c2:/# ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
2: eth0@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:b2:ca:c6 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.0.3.148/24 brd 10.0.3.255 scope global dynamic eth0
       valid_lft 2458sec preferred_lft 2458sec
    inet6 fe80::216:3eff:feb2:cac6/64 scope link 
       valid_lft forever preferred_lft forever
3: eth0.10@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:b2:ca:c6 brd ff:ff:ff:ff:ff:ff
    inet 172.16.1.2/24 scope global eth0.10
       valid_lft forever preferred_lft forever
    inet6 2001:db8::2/64 scope global 
       valid_lft forever preferred_lft forever
    inet6 fe80::216:3eff:feb2:cac6/64 scope link 
       valid_lft forever preferred_lft forever
4: dummy0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether ae:08:23:bc:73:0b brd ff:ff:ff:ff:ff:ff
    inet 10.254.2.1/32 scope global dummy0
       valid_lft forever preferred_lft forever
    inet6 fe80::ac08:23ff:febc:730b/64 scope link 
       valid_lft forever preferred_lft forever

Potřebujeme tedy přidat jeden záznam do směrovací tabulky na c1. Dejme tomu, že z adresního plánu víme, že v naší experimentální máme vyhrazený subnet 10.254.2.0/24 a uděláme tedy statickou route pro tento target (místo jedné konkrétní adresy z tohoto rozsahu). A vyzkoušíme, že nám jeden směr funguje (ping používá source adresu z nejbližšího rozhraní směrem k cíli, takže po nastavení zázamu do směrovací tabulky z jedné strany nám začně v tomto případě vzdálený dummy interface hned odpovídat):

root@c1:/# ip route add 10.254.2.0/24 via 172.16.1.2

root@c1:/# ping 10.254.2.1
PING 10.254.2.1 (10.254.2.1) 56(84) bytes of data.
64 bytes from 10.254.2.1: icmp_seq=1 ttl=64 time=0.297 ms
64 bytes from 10.254.2.1: icmp_seq=2 ttl=64 time=0.168 ms
64 bytes from 10.254.2.1: icmp_seq=3 ttl=64 time=0.251 ms
^C
--- 10.254.2.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 6ms
rtt min/avg/max/mdev = 0.168/0.238/0.297/0.056 ms

A to samé uděláme na na c2 v opačném směru. V tomto případě opět uděláme route šiřší, dle našeho (myšleného) adresního plánu 10.254.1.0/24:

root@c2:/# ip route add 10.254.1.0/24 via 172.16.1.1

root@c2:/# ping 10.254.1.1
PING 10.254.1.1 (10.254.1.1) 56(84) bytes of data.
64 bytes from 10.254.1.1: icmp_seq=1 ttl=64 time=0.362 ms
64 bytes from 10.254.1.1: icmp_seq=2 ttl=64 time=0.176 ms
^C
--- 10.254.1.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3ms
rtt min/avg/max/mdev = 0.176/0.269/0.362/0.093 ms

Chytáky:

  1. Na začátku této sekce jsme zmiňovali sysctl flagy net.ipv4.conf.all.forwarding = 1 pro IPv4 a net.ipv6.conf.all.forwarding = 1. Jenže my jsme toto nastavení zatím nepotřebovali. Trik je v tom, že pokud Linux doručuje datagram, který vznikl lokálně nebo je jeho cílová adresa na některém z rozhraní (klidně i na jiném rozhraní, než na které datagram dorazil), není forwarding flag potřeba a provoz se přesto doručí.

  2. Většina routerů na perimetru ISP sítí vynucuje BCP38 ( https://tools.ietf.org/html/bcp38 ). Ve zkratce jde o to, že pokud má uživatel na svou přípojku přidělenu adresu 192.168.0.100 ze subnetu 192.168.0.0/24, tak nesmí odesílat IP datagramy s (podvrženou) zdrojovou IP adresou mimo přidělený subnet. Takže například adresy se zdrojovou IP ze subnetu 10.254.0.0/16 budou zahozeny. Přesně se této metodě filtrování příchozího provozu říká „(unicast) Reverse Path Filtering„“ neboli uRPF. Aplikace uRPF má sama o sobě velké bezpečností výhody - zabraňuje se tím celé skupině útoků pomocí odrazu o nějakou veřejnou službu, jako je například DNS. Ale má to také negaitvní implikace - znemožňuje to asymetrický routing. V Internetu na úrovni ISP se uRPF už neuplatňuje, protože asymetrický routing je celkem běžná věc. O to důležitější je proto jeho vynucování na perimetru sítě. Podrobněji si to rozebereme v další kapitole. Tady to zmiňujeme proto, že na uRPF je třeba myslet i při vymýšlení routingu.

Odkaz:

Ještě než skončíme s touto kapitolou, stojí za to prověřit si, že nastavení statického IP routingu bezezbytku rozumíme. Podíváme se ještě jednou na směrovací tabulky:

root@c1:/# ip route
default via 10.0.3.1 dev eth0 
10.0.3.0/24 dev eth0 proto kernel scope link src 10.0.3.9 
10.254.2.0/24 via 172.16.1.2 dev eth0.10 
172.16.1.0/24 dev eth0.10 proto kernel scope link src 172.16.1.1

Z pohledu c1 tedy máme lokálně připojený subnet (adresu) 10.254.1.1/32, ten však ve výpisu ip route nevidíme, protože je to jen jedna IP adresa a není proto zapotřebí pro ni vytvářet connected route. Kdybychom použili jinou masku, než /32, tak by connected route automaticky vznikla, podobně jako například pro spojovací síť 172.16.1.0/24. Ale víme, že pro dummy interface nemá smysl jiné nastavení, než pro ně přidělovat a konfigurovat jednotlivé IP adresy.

Chceme zajistit konektivitu na dummy interface na c2, který má adresu 10.254.2.1/32. Musíme tedy přidat záznam do směrovací tabulky na c1, který bude obsahovat tuto adresu.

Protože jsme udělali (dle našeho myšleného adresního plánu) širší routu pro /24 supernet, tak si můžeme položit otázku: Co se stane, když spustíme na c1 ping 10.254.2.2? Přirozeně, že žádné ICMP odpovědi nedostaneme, protože tato adresa není na c2 nastavena. Ale popořádku: Program ping na c1 datagram normálně odešle, protože routa pro cílovou síť existuje, next-hop je validní, nic tomu tedy nebrání. Přesvědčíme se, zda datagram odešel pomocí programutcpdump na c1:

root@c1:/# tcpdump -i eth0.10 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0.10, link-type EN10MB (Ethernet), capture size 262144 bytes
14:00:40.013555 IP 172.16.1.1 > 10.254.2.2: ICMP echo request, id 4328, seq 1559, length 64
14:00:41.037638 IP 172.16.1.1 > 10.254.2.2: ICMP echo request, id 4328, seq 1560, length 64
14:00:42.061234 IP 172.16.1.1 > 10.254.2.2: ICMP echo request, id 4328, seq 1561, length 64
14:00:43.085170 IP 172.16.1.1 > 10.254.2.2: ICMP echo request, id 4328, seq 1562, length 64
14:00:44.109439 IP 172.16.1.1 > 10.254.2.2: ICMP echo request, id 4328, seq 1563, length 64
^C
5 packets captured
5 packets received by filter
0 packets dropped by kernel

Na c2 datagram normálně dorazí (opět ověříme pomocí root@c2:/# tcpdump -i eth0.10 -n). Jenže co dál? Odpověď žádná nechodí (v našem případě). Podívejme se ještě jednou na směrovací tabulku na c2:

root@c2:/# ip route
default via 10.0.3.1 dev eth0 
10.0.3.0/24 dev eth0 proto kernel scope link src 10.0.3.148 
10.254.1.0/24 via 172.16.1.1 dev eth0.10 
172.16.1.0/24 dev eth0.10 proto kernel scope link src 172.16.1.2

Protože adresa 10.254.2.2 není nastavena na žádném rozhraní c2, uplatní se tedy jediný route záznam, který matchuje a to je default route. Takže datagram odchází směrem do Internetu (tedy na VM). To si ověříme opět pomocí tcpdump (tentokrát nedumpujeme na eth0.10, ale na eth0):

root@c2:/# tcpdump -i eth0 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
14:07:02.989519 IP 172.16.1.1 > 10.254.2.2: ICMP echo request, id 4328, seq 1933, length 64
14:07:02.989566 IP 172.16.1.1 > 10.254.2.2: ICMP echo request, id 4328, seq 1933, length 64
14:07:04.013649 IP 172.16.1.1 > 10.254.2.2: ICMP echo request, id 4328, seq 1934, length 64
14:07:04.013703 IP 172.16.1.1 > 10.254.2.2: ICMP echo request, id 4328, seq 1934, length 64
^C
4 packets captured
4 packets received by filter
0 packets dropped by kernel

Takže naše pátrání bychom měli přesunout na VM a pravděpodobně zjistíme, že VM datagram přepošle dál podle své defaut route a teprve naše gateway v síti, do které je připojena naše pracovní stanice, datagram zahodí na základě uRPF. A skutečně, tcpdump na VM ukazuje, že tyto datagramy odcházejí směrem do Internetu, jak předepisují default route:

root@osboxes:/home/osboxes# tcpdump -i enp0s3 icmp
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on enp0s3, link-type EN10MB (Ethernet), capture size 262144 bytes
14:25:15.959577 IP 172.16.1.1 > 10.254.2.2: ICMP echo request, id 4328, seq 2998, length 64
14:25:16.983333 IP 172.16.1.1 > 10.254.2.2: ICMP echo request, id 4328, seq 2999, length 64
14:25:18.007433 IP 172.16.1.1 > 10.254.2.2: ICMP echo request, id 4328, seq 3000, length 64
^C
3 packets captured
3 packets received by filter
0 packets dropped by kernel

Je trochu škoda, že náš poskytovatel Internetu dodává closed-source routery, do kterých se nedá podívat, takže samotné zahození na uRPF nyní nemůžeme demonstrovat. Ale dostaneme se k tomu v další kapitole, kde si tento scénář ještě rozpracujeme.