[Gateway] GRE-Tunnel natten


#1

Moin,

hier kurz eine kleine Doku zum Natten von GRE-Tunneln. Ich wollte kürzlich ein nicht virtualisieres Gateway virtualisieren. Damit ich Lars nicht um eine IP-Änderungen bemühen muss, hab ich einfach mal probiert die Tunnel zu natten, das geht problemlos.

GRE-Nat-Module:

# cat /etc/modules
nf_nat_proto_gre
nf_conntrack_proto_gre

Und in den iptables:

-A PREROUTING -i eth0 -p gre -j DNAT --to-destination 192.168.10.35

Masquerade nicht vergessen:

-A POSTROUTING -s 192.168.10.0/24 ! -d 192.168.10.0/24 -j MASQUERADE

Für L2TP und SSH ggfs. noch weitere Ports durchnatten:

-A PREROUTING -d 176.9.38.150/32 -p tcp -m tcp --dport 222 -j DNAT --to-destination 192.168.10.35:22
-A PREROUTING -d 176.9.38.150/32 -p udp -m udp --dport 20000:20100 -j DNAT --to-destination 192.168.10.35:20000-20100

Nicht vergessen, die standardmäßige ICMP/Ping-Sperre rauszunehmen, sonst geht’s irgendwie nicht.

Und die Tunnel müssen dann lokal mit der lokalen IP gebaut werden, Beispiel:

auto tun-ffrl-dus0
iface tun-ffrl-dus0 inet static
        address 100.64.7.185
        netmask 255.255.255.254
        pre-up ip tunnel add $IFACE mode gre local 192.168.10.35 remote 185.66.193.0 ttl 255
        post-up ip link set $IFACE mtu 1400
        post-down ip tunnel del $IFACE
        post-up ip rule add iif $IFACE lookup ffnet
        pre-down ip rule del iif $IFACE lookup ffnet ||:
iface tun-ffrl-dus0 inet6 static
        address 2a03:2260:0:3f8::2
        netmask 64
        post-up ip -6 rule add iif $IFACE lookup ffnet
        pre-down ip -6 rule del iif $IFACE lookup ffnet ||:

Wenn man mit Ansible arbeitet, kann man da die eth0-IP automatisch unter v4_remote abrufen. Das ist in unseren Rollen so hinterlegt.

Eine Durchsatzmessung auf leerem Gateway lag bei ungefähr 900 Mbit/s über L2TP von einer Maschine an einem virtuellen x86-Knoten und dann über das FFRL-Backbone (getestet mit Speedtest.net).

Dies ist ein Wiki, sollte ich etwas vergessen haben, gerne ergänzen.

Grüße
Matthias


#2

Das funktionierte einige Monate total super und hat jetzt im Januar quasi gleichzeitig auf zwei Kisten aufgehört zu funktionieren.

Ich habe gerade die Kernel 4.4.113, 4.13.0-35 und 4.13.0-37, sowie 4.14 probiert. Entweder hat ein Sicherheitspatch, den alle bekommen haben, das in allen Versionen gleichzeitig zerstört oder irgendwas anderes ist kaputt gegangen.

Hat das jemand im Einsatz und hat ähnliche Erfahrungen gemacht oder es läuft noch?


#3

Ich kann leider nichts zum GRE Problem sagen, allerdings wäre VXLAN oder L2TPv3 bei NAT die Tunnelprotokolle der Wahl, da sie in UDP verkapselt sind und damit rout- und NATbar sind.
Hatten die Diskussion gestern noch im #gluon Channel im IRC.
Vielleicht sollte man das Backbone Team bitten alternativ auch diese Art der Tunnel anzubieten.


#4

Ich hab das etwas diagnostiziert und werde mal auf einer Kernelmailingliste nachfragen. Manche Pakete werden genattet, andere nicht. Halte ich für einen Bug.


#5

Irgendwie habe ich schon wieder das Gefühl, dass wir hier beim beim Gluon-Freifunk “zu viel komisches Zeug” mit dem Linux veranstalten, was sonst niemand tut.

vgl. Batman-adv-bugs “double MTU refragmentation”-packet-drop,
vgl. L2TP-bugs “doublicated tunnel id” beim tunneldigger…


#6
root@unimatrixzero ~ # tcpdump -ni any host 185.66.195.0 and \( host 176.9.38.150 or host 192.168.10.62 \) and proto 47 and ip[33]=0x01 and \( ip[36:4]==0x644007B4 or ip[40:4]==0x644007B4 \)
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
03:58:01.972551 IP 192.168.10.62 > 185.66.195.0: GREv0, length 88: IP 185.66.194.49 > 100.64.7.180: ICMP echo request, id 25043, seq 1, length 64
03:58:01.972554 IP 192.168.10.62 > 185.66.195.0: GREv0, length 88: IP 185.66.194.49 > 100.64.7.180: ICMP echo request, id 25043, seq 1, length 64
03:58:03.001013 IP 192.168.10.62 > 185.66.195.0: GREv0, length 88: IP 185.66.194.49 > 100.64.7.180: ICMP echo request, id 25043, seq 2, length 64
03:58:03.001021 IP 192.168.10.62 > 185.66.195.0: GREv0, length 88: IP 185.66.194.49 > 100.64.7.180: ICMP echo request, id 25043, seq 2, length 64

Wie man der Ausgabe entnehmen kann, kommt das Paket aus dem Interface, wandert durch die Brücke für die genatteten VMs, wird aber auf dem Blech nicht durch das GRE-Nat geschickt. Man müsste hier noch eine dritte Zeile sehen, dann mit der Quell-IP des Blechs. Das ist auch der Fall, wenn man durch den funktionierenden Tunnel pingt.

Hier nochmal zum Vergleich wie es bei einem korrekt funktionierenden NAT zu Ber1 aussieht:

root@unimatrixzero ~ # tcpdump -ni any host 185.66.195.1 and \( host 176.9.38.150 or host 192.168.10.62 \) and proto 47 and ip[33]=0x01 and \( ip[36:4]==0x644007BA or ip[40:4]==0x644007BA \)
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
04:06:41.322914 IP 192.168.10.62 > 185.66.195.1: GREv0, length 88: IP 185.66.194.49 > 100.64.7.186: ICMP echo request, id 26639, seq 1, length 64
04:06:41.322922 IP 192.168.10.62 > 185.66.195.1: GREv0, length 88: IP 185.66.194.49 > 100.64.7.186: ICMP echo request, id 26639, seq 1, length 64
04:06:41.322928 IP 176.9.38.150 > 185.66.195.1: GREv0, length 88: IP 185.66.194.49 > 100.64.7.186: ICMP echo request, id 26639, seq 1, length 64
04:06:41.341906 IP 185.66.195.1 > 176.9.38.150: GREv0, length 88: IP 100.64.7.186 > 185.66.194.49: ICMP echo reply, id 26639, seq 1, length 64
04:06:41.341915 IP 185.66.195.1 > 192.168.10.62: GREv0, length 88: IP 100.64.7.186 > 185.66.194.49: ICMP echo reply, id 26639, seq 1, length 64
04:06:41.341918 IP 185.66.195.1 > 192.168.10.62: GREv0, length 88: IP 100.64.7.186 > 185.66.194.49: ICMP echo reply, id 26639, seq 1, length 64
04:06:42.324228 IP 192.168.10.62 > 185.66.195.1: GREv0, length 88: IP 185.66.194.49 > 100.64.7.186: ICMP echo request, id 26639, seq 2, length 64
04:06:42.324234 IP 192.168.10.62 > 185.66.195.1: GREv0, length 88: IP 185.66.194.49 > 100.64.7.186: ICMP echo request, id 26639, seq 2, length 64
04:06:42.324243 IP 176.9.38.150 > 185.66.195.1: GREv0, length 88: IP 185.66.194.49 > 100.64.7.186: ICMP echo request, id 26639, seq 2, length 64
04:06:42.343154 IP 185.66.195.1 > 176.9.38.150: GREv0, length 88: IP 100.64.7.186 > 185.66.194.49: ICMP echo reply, id 26639, seq 2, length 64
04:06:42.343163 IP 185.66.195.1 > 192.168.10.62: GREv0, length 88: IP 100.64.7.186 > 185.66.194.49: ICMP echo reply, id 26639, seq 2, length 64
04:06:42.343165 IP 185.66.195.1 > 192.168.10.62: GREv0, length 88: IP 100.64.7.186 > 185.66.194.49: ICMP echo reply, id 26639, seq 2, length 64

Also die ausgehenden Pakete gehen schon verloren, weil sie nicht genattet werden. Und halt nicht einheitlich. Es scheint ähnlich dem L2TP-Tunneldiggerproblem so zu sein, dass nur eine Verbindung geht, aber keine zweite.

Die beiden Tests sind entstanden, ohne dass irgendwas verändert wurde.

Grüße
Matthias


#7

Hm, wie oben zu sehen wird hier GREv0 genutzt. Dazu aus dem Quelltext:

switch (greh->flags & GRE_VERSION) {
case GRE_VERSION_0:
	/* We do not currently NAT any GREv0 packets.
	 * Try to behave like "nf_nat_proto_unknown" */
	break;
case GRE_VERSION_1:
	pr_debug("call_id -> 0x%04x\n", ntohs(tuple->dst.u.gre.key));
	pgreh->call_id = tuple->dst.u.gre.key;
	break;
default:
	pr_debug("can't nat unknown GRE version\n");
	return false;
} 

Quelle. Ich habe gerade versucht herauszufinden, was genau der Unterschied zwischen GREv0 und GREv1 ist. Finde ich leider nicht.

Ich frage mich, wie das jemals funktioniert hat.


#8

Ich hab jetzt irgendwie trotz viel Googlens nicht rausgefunden, was der Unterschied zwischen GREv0 und GREv1 ist.

Ich hätte spontan drauf getippt, dass GREv1 für IPV6 ist, aber das passt nicht dazu, dass es dafür ein Nat-Modul gibt. Da bräuchte man das eher nicht.

Evtl. haben @Lars oder @takt mehr Infos dazu? Mir scheint, als ob GRE-Nat eigentlich gar nicht implementiert ist und dass es bisher nur durch ein Fallback auf das proto_unknown funktioniert hat.


#9

Wer suchet, der findet: RFC2784 Abschnitt 7.1

7.1.  GRE Version Numbers

   This document specifies GRE version number 0. GRE version number 1 is
   used by PPTP [RFC2637]. Additional GRE version numbers are assigned
   by IETF Consensus as defined in RFC 2434 [RFC2434].

Also wenn ich das mit dem Abschnitt aus dem Quellcode kombinieren, lief GRE-Nat bisher über irgendwelche Rückfallfunktionen und ist eigentlich nicht implementiert.


#10

d.h. auf “namenlosen” alten Kerneln hat das nur “zufällig” (und sogar hinreichend performant und ohne signifikanten packetloss) funktioniert?


#11

Ich konnte es mit 4.4.113 nicht wieder ans Laufen bekommen. Daher ist mir nicht ganz klar, was das Problem eigentlich verursacht.


#12

Setzt du bei den GRE Tunneln denn jeweils einen unterschiedlichen Key?


#13

Das ist nur erforderlich, wenn die Start- und Zieladresse bei mehreren Tunneln gleich ist. Dieser Schlüssel ist eine Art Port wie bei TCP oder UDP.

Wenn man Tunnel zu verschiedenen Adressen aufbaut, benötigt man die nicht.


#14

Nunja, ich glaube, mit batman_adv machen wir schon sehr viel komisches Zeug auf verschiedenen Netzwerkebenen, da darf man sich über lustige Effeke nicht wundern.

Nunja, das Probem ist aber batman-adv-intern (also insbes. nicht Linux-spezifisch), oder? Und MTU-Probleme stehen ja zu erwarten:

[   27.875416] batman_adv: bat-ffgt: Adding interface: Egw00
[   27.875419] batman_adv: bat-ffgt: The MTU of interface Egw00 is too small (1500) to handle the transport of batman-adv packets. Packets going over this interface will be fragmented on layer2 which could impact the performance. Setting the MTU to 1528 would solve the problem.
[   27.875421] batman_adv: bat-ffgt: Interface activated: Egw00

Das muß batman_adv.ko schon korrekt selbst lösen, sprich im Grunde korrekt frag- und auf den anderen Seite defragmentieren.

Das wiederum ist “nur” ein RTFM-Problem, die Session-ID war als systemweit eindeutig vorgesehen (»set the session id, which is a 32-bit integer value. Uniquely identifies the session being created. The value used must match the peer_session_id value being used at the peer«), das war älteren Kerneln bislang halt egal. Die Idee ist wohl, daß anhand der Session-ID die Daten einem Tunnel eindeutig zugeordnet werden können; aber auch das hat mit Gluon oder Linux per se zu tun. Das ist/war ein Bug in Tunneldigger; eine Abkürzung, die zufällig in älteren Linux-Kerneln funktionierte.

Aber zum Thema zurück, in abgewandelter Form gebe ich @adorfer recht: »Damit ich Lars nicht um eine IP-Änderungen bemühen muss, hab ich einfach mal probiert die Tunnel zu natten, das geht problemlos. […] Das funktionierte einige Monate total super und hat jetzt im Januar quasi gleichzeitig auf zwei Kisten aufgehört zu funktionieren.«
Hier fehlt imho der zweite Schritt, nämlich nach der Konfiguration von NAT dieses überflüssig zu machen. Denn das macht das ohnehin schon komplexe Setup nur – unnötig – komplizierter und, wie man sieht, fehleranfälliger.

Aus diesem Grunde, dem KISS-Prinzip folgend, setzen wir vorzugsweise auf L2TPv3 (»Ethernet Psedowires«), da sich das wie ein Ethernet-Interface verhält.


#15

@wusel, soll ich das einfach mal ausgliedern, falls ja unter welchem Namen? Es geht hier wieder um L2TP noch um Batman, sondern um GRE-Tunnel.

Und NAT ist nichts kompliziertes. Simples Austauschen von 32 bit in einem Paket.


#16

Kannst Du gerne ausgliedern, latent wäre ein “Erklärbar” über “Unterschiede der verschiedenen Tunneltechniken” oder so evtl. hilfreich (in-kernel vs. Userspace; tun vs. tap; L2 vs. L3; IP- vs. UDP- vs. TCP-based vs. eigenes IP-Protokoll (IPSec); …)

»Und NAT ist nichts kompliziertes. Simples Austauschen von 32 bit in einem Paket.«

Das stimmt für das IP-Paket. Sobald das verwendete höhere Protokoll ebenfalls mit IP-Adressen arbeitet, wird es alles andere als »simpel«, denn dann müssen diese »32 bit« an ggf. weiteren Stellen im Datenpaket geändert werden. (Vgl. auch die speziellen nat- und conntrack-Module; wäre es »that simple«, bräuchte es keine spezialisierten Module zur Unterstüzung.)

NAT ist auch teuer: NAT bedeutet, daß ein Paket nicht einfach weitergeleitet werden kann, sondern durch die CPU zerpflückt, verändert und neu zusammengesetzt werden muß. In Deinem Fall ist die Anwendung von NAT absolut unnötig, und die Lösung des Problem trivial. In diesem konkreten Fall machst Du im Grunde cat <file | sort | cat >file.sort, wo sort <file >file.sort reichen würde: es geht (meistens; außer es wäre kein Hauptspeicher mehr frei für 2x cat & 1x sort parallel), aber es ist höllisch ineffizient.

Cool finde ich die Nutzung von »-i any« im tcpdump, wäre ich so nicht drauf gekommen. Vielleicht auch, weil ich mich gleichzeitig frage. ob es eine gute Idee ist, alle Interfaces in den promiskuitiven Modus zu schalten.


#17

Berechtigt. Gibt wohl demnächst einen Patch, dass tcpdump ausgeben kann, auf welchem Interface ein Paket abgefangen wurde. Das kann es bisher noch nicht.

Siehe Quellcode oben, GRE ist so simpel, dass das NAT nichtmals implementiert wurde, sondern ein Fallback auf irgendeine Standardfunktion gebaut wurde.

Außerdem machen wir ja Freifunk nicht, weil es einfach ist, sondern weil es geht ;).


#18

in 95% der Fälle will man tcpdump sowieso ohne promisc benutzen, indem man “-p” verwendet.


#19

Ein paar neue Infos hierzu: https://forum.freifunk-muensterland.de/t/admintagebuch-dokumentation-der-admintatigkeiten/75/1215?u=mpw