Angesichts der Erschöpfung der IPv4-Adressen stehen viele Netzwerkbetreiber vor der Herausforderung, den Zugang ihrer Kunden über Adressübersetzung zu ermöglichen. In diesem Artikel erkläre ich, wie Sie Carrier-Grade-NAT-Leistung auf handelsüblichen Servern erzielen können.
Ein wenig Geschichte
Das Thema der Erschöpfung des IPv4-Adressraums ist längst nicht neu. Irgendwann entstanden in RIPE Wartelisten, gefolgt von Marktplätzen, auf denen Adressblöcke gehandelt und Mietverträge abgeschlossen wurden. Allmählich begannen die Telekommunikationsanbieter, Internetzugang über Adress- und Portübersetzung anzubieten. Während einige nicht genug Adressen erhielten, um jedem Teilnehmer eine 'weiße' Adresse zuzuweisen, begannen andere, Kosten zu sparen, indem sie auf den Kauf von Adressen auf dem Sekundärmarkt verzichteten. Hersteller von Netzwerkgeräten unterstützten diese Idee, da diese Funktionalität in der Regel zusätzliche Erweiterungsmodule oder Lizenzen erfordert. Bei Juniper beispielsweise kann NAPT bei den MX-Routern (außer den neuesten MX104 und MX204) auf einer separaten Servicekarte MS-MIC durchgeführt werden, während für den Cisco ASR1k eine SGN-Lizenz erforderlich ist und für den Cisco ASR9k ein separates Modul A9K-ISM-100 sowie die Lizenz A9K-CGN-LIC erforderlich sind. Insgesamt kostet der Spaß eine Menge Geld.
IPTables
Die Aufgabe der NAT-Implementierung erfordert keine speziellen Rechenressourcen; sie kann von Standardprozessoren erledigt werden, wie sie beispielsweise in jedem Heimrouter zu finden sind. Im Maßstab eines Telekommunikationsanbieters lässt sich diese Aufgabe mit Commodity-Servern, die mit FreeBSD (ipfw/pf) oder GNU/Linux (iptables) betrieben werden, lösen. Wir werden FreeBSD nicht weiter betrachten, da ich seit Langem auf diese OS verzichtet habe, und uns stattdessen auf GNU/Linux konzentrieren.
Die Aktivierung der Adressübersetzung ist ganz unkompliziert. Zuerst müssen Sie eine Regel in iptables in der nat-Tabelle festlegen:
iptables -t nat -A POSTROUTING -s 100.64.0.0/10 -j SNAT --to - --persistent
Das Betriebssystem wird das Modul nf_conntrack laden, das alle aktiven Verbindungen überwacht und die erforderlichen Transformationen vornimmt. Hier gibt es einige Feinheiten. Erstens, da es sich um NAT im Maßstab eines Telekommunikationsanbieters handelt, müssen die Timeout-Werte angepasst werden, da die Standardwerte dazu führen, dass die Größe der Übersetzungstabelle schnell katastrophale Ausmaße annimmt. Im Folgenden finden Sie ein Beispiel für die Einstellungen, die ich auf meinen Servern verwendet habe:
net.ipv4.ip_forward = 1
net.ipv4.ip_local_port_range = 8192 65535
net.netfilter.nf_conntrack_generic_timeout = 300
net.netfilter.nf_conntrack_tcp_timeout_syn_sent = 60
net.netfilter.nf_conntrack_tcp_timeout_syn_recv = 60
net.netfilter.nf_conntrack_tcp_timeout_established = 600
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 45
net.netfilter.nf_conntrack_tcp_timeout_last_ack = 30
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_close = 10
net.netfilter.nf_conntrack_tcp_timeout_max_retrans = 300
net.netfilter.nf_conntrack_tcp_timeout_unacknowledged = 300
net.netfilter.nf_conntrack_udp_timeout = 30
net.netfilter.nf_conntrack_udp_timeout_stream = 60
net.netfilter.nf_conntrack_icmpv6_timeout = 30
net.netfilter.nf_conntrack_icmp_timeout = 30
net.netfilter.nf_conntrack_events_retry_timeout = 15
net.netfilter.nf_conntrack_checksum=0
Und zweitens, da die Standardgröße der Übersetzungstabelle nicht für den Betrieb eines Netzwerkbetreibers ausgelegt ist, muss sie erhöht werden:
net.netfilter.nf_conntrack_max = 3145728
Es ist auch erforderlich, die Anzahl der Buckets für die Hash-Tabelle, die alle Übersetzungen speichert (dies ist eine Option des nf_conntrack-Moduls), zu erhöhen:
options nf_conntrack hashsize=1572864
Nach diesen einfachen Schritten entsteht eine funktionierende Konstruktion, die in der Lage ist, eine große Anzahl an Client-Adressen in einen Pool von externen Adressen zu übertragen. Jedoch lässt die Leistung dieser Lösung zu wünschen übrig. In meinen ersten Versuchen mit GNU/Linux für NAT (um das Jahr 2013) konnte ich eine Leistung von etwa 7 Gbit/s bei 0,8 Mpps auf einem Server (Xeon E5-1650v2) erreichen. Seitdem wurden im Netzwerk-Stack des GNU/Linux-Kernels viele Optimierungen vorgenommen, und die Leistung eines Servers mit derselben Hardware hat sich nahezu auf 18-19 Gbit/s bei 1,8-1,9 Mpps erhöht (das waren die Grenzwerte). Dennoch wuchs der Bedarf an dem Volumen an Datenverkehr, der von einem Server verarbeitet werden kann, weitaus schneller. Schließlich wurden Lastverteilungsschemata auf verschiedene Server entwickelt, was jedoch die Komplexität der Einrichtung, Wartung und Gewährleistung der Dienstleistungsqualität erhöhte.
NFTables
Ein derzeit beliebter Trend im Bereich der Programmierung von "Paketverlagerungen" ist die Verwendung von DPDK und XDP. Zu diesem Thema sind zahlreiche Artikel verfasst worden, es gab viele verschiedene Präsentationen, und kommerzielle Produkte wie SKAT von VasExperts erscheinen. Doch in Anbetracht der begrenzten Ressourcen an Programmierern bei den Telekommunikationsanbietern ist es ziemlich problematisch, selbstständig etwas auf Basis dieser Frameworks zu entwickeln. Ein solches Lösungssystem später zu betreiben wird erheblich komplizierter, da unter anderem Diagnosewerkzeuge entwickelt werden müssen. Beispielsweise wird das Standardwerkzeug tcpdump mit DPDK nicht einfach funktionieren, und die Pakete, die mit XDP zurück in die Leitungen gesendet werden, kann es nicht "sehen". Vor dem Hintergrund aller Diskussionen über neue Technologien zur Packet-Forwarding im User-Space sind unbemerkt geblieben und von Pablo Neira Ayuso, dem Maintainer von iptables, über die Entwicklung von Flow Offloading in nftables. Lassen Sie uns diesen Mechanismus näher betrachten.
Die grundsätzliche Idee ist, dass wenn ein Router Pakete einer Sitzung in beide Richtungen des Stroms durchlässt (die TCP-Sitzung befindet sich im Zustand ESTABLISHED), es nicht notwendig ist, nachfolgende Pakete dieser Sitzung durch alle Firewall-Regeln zu lassen, da all diese Prüfungen sowieso damit enden, dass das Paket weiter zum Routing weitergeleitet wird. Und tatsächlich muss die Routenwahl nicht mehr durchgeführt werden – wir wissen bereits, auf welches Interface und an welchen Host die Pakete innerhalb dieser Sitzung weitergeleitet werden müssen. Es bleibt nur, diese Informationen zu speichern und sie frühzeitig bei der Paketverarbeitung für das Routing zu verwenden. Bei der Durchführung von NAT muss zusätzlich die Information über die Änderungen der Adressen und Ports, die durch das nf_conntrack-Modul umgewandelt wurden, gespeichert werden. Ja, natürlich funktionieren in diesem Fall verschiedene Policer und andere informationsstatistische Regeln in iptables nicht mehr, aber im Rahmen der Aufgabe eines einzelnen NAT oder beispielsweise eines Borders ist das nicht so wichtig, da die Dienste auf verschiedene Geräte verteilt sind.
Konfiguration
Um diese Funktion nutzen zu können, müssen wir:
- Verwenden Sie einen aktuellen Kernel. Obwohl diese Funktionalität bereits im Kernel 4.16 verfügbar war, war sie lange Zeit sehr "roh" und führte regelmäßig zu Kernel-Paniken. Stabilisiert hat sich alles ungefähr im Dezember 2019, als die LTS-Kernels 4.19.90 und 5.4.5 veröffentlicht wurden.
- Schreiben Sie die iptables-Regeln ins nftables-Format um, indem Sie eine ausreichend aktuelle Version von nftables verwenden. Funktioniert in Version 0.9.0 einwandfrei.
Wenn der erste Punkt im Grunde klar ist, ist es wichtig, das Modul bei der Konfiguration während des Kompilierens einzuschließen (CONFIG_NFT_FLOW_OFFLOAD=m). Der zweite Punkt benötigt jedoch eine Erklärung. Die Regeln von nftables werden ganz anders beschrieben als in iptables. deckt praktisch alle Punkte ab, zudem gibt es spezielle von iptables zu nftables. Daher werde ich nur ein Beispiel für die NAT- und Flow-Offload-Konfiguration geben. Eine kleine Legende für das Beispiel: , – dies sind die Netzwerkinterfaces, über die der Datenverkehr läuft; tatsächlich können es mehr als zwei sein. , – die Anfangs- und Endadresse des Bereichs der "weißen" Adressen.
Die NAT-Konfiguration ist sehr einfach:
#! /usr/sbin/nft -f
table nat {
chain postrouting {
type nat hook postrouting priority 100;
oif <o_if> snat to <pool_addr_start>-<pool_addr_end> persistent
}
}
Mit dem Flow-Offload ist es etwas komplizierter, aber durchaus verständlich:
#! /usr/sbin/nft -f
table inet filter {
flowtable fastnat {
hook ingress priority 0
devices = { <i_if>, <o_if> }
}
chain forward {
type filter hook forward priority 0; policy accept;
ip protocol { tcp , udp } flow offload @fastnat;
}
}
Das ist eigentlich die ganze Einrichtung. Jetzt wird der gesamte TCP/UDP Verkehr in die fastnat-Tabelle geleitet und viel schneller verarbeitet.
Ergebnisse
Um zu verdeutlichen, wie viel «schneller» es tatsächlich ist, füge ich einen Screenshot der Last zweier realer Server bei, die mit identischen Komponenten (Xeon E5-1650v2), gleich konfiguriert und dasselbe Linux-Kernel verwenden, jedoch NAT über iptables (NAT4) und nftables (NAT5) durchführen.

Im Screenshot fehlt das Diagramm der Pakete pro Sekunde, aber im Lastprofil dieser Server liegt die durchschnittliche Paketgröße bei etwa 800 Bytes, wodurch Werte von bis zu 1,5 Mpps erreicht werden. Wie zu sehen ist, hat der Server mit nftables eine enorme Leistungskapazität. Momentan verarbeitet dieser Server bis zu 30 Gbit/s bei 3 Mpps und ist eindeutig in der Lage, das physische Limit des Netzwerks von 40 Gbps zu erreichen, während er dabei über ausreichende CPU-Ressourcen verfügt.
Ich hoffe, dieses Material wird Netzwerkingenieuren nützlich sein, die versuchen, die Leistung ihrer Server zu verbessern.
Quelle: habr.com
