Omdat de IPv4-adressen op zijn, moeten veel telecombedrijven hun klanten netwerktoegang bieden met behulp van adresvertaling. In dit artikel leg ik uit hoe u Carrier Grade NAT-prestaties op standaardservers kunt behalen.
Een beetje geschiedenis
Het probleem van het opraken van de IPv4-adresruimte is niet nieuw. Op een gegeven moment introduceerde RIPE wachtlijsten en ontstonden er beurzen waar blokken adressen werden verhandeld en deals werden gesloten om ze te verhuren. Geleidelijk aan begonnen telecombedrijven internettoegangsdiensten aan te bieden met behulp van adres- en poortvertaling. Sommigen hadden niet de tijd om voldoende adressen te verkrijgen om aan elke abonnee een ‘wit’ adres te verstrekken, en sommigen begonnen geld te besparen door te weigeren adressen op de secundaire markt te kopen. Fabrikanten van netwerkapparatuur steunden dit idee omdat voor deze functionaliteit doorgaans extra uitbreidingsmodules of licenties nodig zijn. Zo kan NAPT in de Juniper MX-routerlijn (behalve de nieuwste MX104 en MX204) worden uitgevoerd op een aparte MS-MIC-servicekaart, is op de Cisco ASR1k een CGN-licentie vereist en op de Cisco ASR9k een aparte A9K-ISM-100-module en een A9K-CGN-LIC-licentie daarvoor. Over het algemeen kost het plezier veel geld.
IPTabellen
Voor het uitvoeren van NAT zijn geen gespecialiseerde computerbronnen nodig; Dit probleem kan worden opgelost met behulp van universele processoren, die bijvoorbeeld in elke thuisrouter zijn geïnstalleerd. Op de schaal van een telecombedrijf kan dit probleem worden opgelost met standaardservers die draaien op FreeBSD (ipfw/pf) of GNU/Linux (iptables). FreeBSD laten we hier buiten beschouwing, omdat ik dit besturingssysteem al lang niet meer gebruik. Laten we het bij GNU/Linux houden.
Het inschakelen van adresvertaling is helemaal niet moeilijk. Eerst moet u een regel in iptables schrijven voor de nat-tabel:
iptables -t nat -A POSTROUTING -s 100.64.0.0/10 -j SNAT --to <pool_start_addr>-<pool_end_addr> --persistent
Het besturingssysteem laadt de module nf_conntrack, die alle actieve verbindingen controleert en de nodige transformaties uitvoert. Er zijn hier enkele subtiliteiten. Ten eerste is het, aangezien we het hebben over NAT op de schaal van een telecomoperator, noodzakelijk om de time-outs aan te passen, omdat met de standaardwaarden de omvang van de vertaaltabel snel tot catastrofale waarden zal groeien. Hieronder ziet u een voorbeeld van de instellingen die ik op mijn servers heb gebruikt:
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
En ten tweede, omdat de standaardgrootte van de vertaaltabel niet is ontworpen om te werken in de omstandigheden van de telecomoperator, moet deze worden vergroot:
net.netfilter.nf_conntrack_max = 3145728
Het is ook nodig om het aantal buckets voor de hashtabel waarin alle uitzendingen worden opgeslagen, te verhogen (dit is een optie van de nf_conntrack-module):
options nf_conntrack hashsize=1572864
Na deze eenvoudige handelingen krijgt u een volledig functioneel ontwerp dat een groot aantal clientadressen naar een externe pool kan uitzenden. De prestaties van deze oplossing laten echter veel te wensen over. Bij mijn eerste pogingen om GNU/Linux voor NAT te gebruiken (rond 2013) kon ik prestaties van ongeveer 7 Gbit/s bij 0.8 Mpps behalen op één server (Xeon E5-1650v2). Sindsdien zijn er veel verschillende optimalisaties doorgevoerd in de GNU/Linux-kernelnetwerkstack. De prestaties van één server op dezelfde hardware zijn gestegen tot bijna 18-19 Gbit/s bij 1.8-1.9 Mpps (dit waren de maximale waarden), maar de behoefte aan het volume aan verkeer dat door één server kan worden verwerkt, is veel sneller gegroeid. Er werden dus schema's ontwikkeld om de belasting over verschillende servers te verdelen. Dit alles verhoogde echter de complexiteit van het opzetten, onderhouden en handhaven van de kwaliteit van de geleverde diensten.
NFT-tabellen
Een modieuze trend in software "packet shifting" is het gebruik van DPDK en XDP. Er zijn veel artikelen over dit onderwerp geschreven, er zijn veel verschillende toespraken gehouden en er verschijnen commerciële producten (bijvoorbeeld SKAT van VasExperts). Maar gezien de beperkte middelen van de programmeurs van telecombedrijven is het nogal problematisch om op basis van deze raamwerken zelfstandig een soort ‘product’ te creëren. Het zal in de toekomst veel moeilijker zijn om een dergelijke oplossing te gebruiken; Er zullen met name diagnostische instrumenten ontwikkeld moeten worden. De standaard tcpdump met DPDK zal bijvoorbeeld niet zomaar werken en zal de pakketten die met XDP naar de kabels worden teruggestuurd, niet ‘zien’. Te midden van alle gesprekken over nieuwe technologieën voor het doorsturen van pakketten naar de gebruikersruimte, zijn sommige ervan onopgemerkt gebleven и Pablo Neira Ayuso, iptables-beheerder, over de ontwikkeling van flow offloading in nftables. Laten we dit mechanisme eens nader bekijken.
Het basisidee is dat als de router pakketten van één sessie in beide richtingen van de stroom heeft doorgelaten (de TCP-sessie is in de status ESTABLISHED gekomen), het niet nodig is om daaropvolgende pakketten van deze sessie door alle firewallregels te sturen, omdat al deze controles er nog steeds mee eindigen dat het pakket naar de routering wordt doorgestuurd. En het is niet nodig om daadwerkelijk een route te selecteren: we weten al naar welke interface en naar welke host de pakketten binnen deze sessie moeten worden verzonden. Het enige dat nu nog rest, is het opslaan van deze informatie en het gebruiken ervan voor routering in een vroeg stadium van pakketverwerking. Bij het uitvoeren van NAT is het noodzakelijk om ook informatie op te slaan over wijzigingen in adressen en poorten die door de nf_conntrack-module zijn geconverteerd. Ja, natuurlijk werken in dit geval verschillende policers en andere informatie-statistische regels in iptables niet meer, maar in het kader van de taak van een aparte NAT of bijvoorbeeld een border is dit niet zo belangrijk, omdat de diensten over apparaten verdeeld zijn.
Configuratie
Om deze functie te gebruiken hebben we het volgende nodig:
- Gebruik verse kernel. Ondanks het feit dat de functionaliteit zelf in kernel 4.16 verscheen, was deze lange tijd nog erg ‘rauw’ en veroorzaakte regelmatig kernel panic. Alles stabiliseerde rond december 2019, toen LTS-kernels 4.19.90 en 5.4.5 werden uitgebracht.
- Herschrijf iptables-regels naar nftables-formaat met behulp van een vrij recente versie van nftables. Werkt exact in versie 0.9.0
Als het eerste punt in principe duidelijk is, is het belangrijkste om niet te vergeten de module tijdens de assemblage in de configuratie op te nemen (CONFIG_NFT_FLOW_OFFLOAD=m). Vervolgens behoeft het tweede punt enige uitleg. De regels in nftables worden op een compleet andere manier beschreven dan in iptables. onthult bijna alle punten, er zijn ook speciale regels van iptables naar nftables. Daarom geef ik alleen een voorbeeld van het instellen van NAT en flow offload. Een kleine legende bijvoorbeeld: , — dit zijn netwerkinterfaces waarlangs het verkeer passeert, in werkelijkheid kunnen er meer dan twee zijn. , — de begin- en eindadressen van het bereik van “witte” adressen.
NAT-configuratie is heel eenvoudig:
#! /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
}
}
Bij flow offload is het iets ingewikkelder, maar wel duidelijk:
#! /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;
}
}
Dat is in principe alle voorbereiding. Vanaf nu wordt al het TCP/UDP-verkeer naar de fastnat-tabel gestuurd en veel sneller verwerkt.
Bevindingen
Om duidelijk te maken hoeveel sneller dit is, voeg ik een screenshot toe van de belasting op twee echte servers, met dezelfde hardware (Xeon E5-1650v2), dezelfde configuratie, dezelfde Linux-kernel, maar met NAT in iptables (NAT4) en in nftables (NAT5).

In de schermafbeelding is geen grafiek van het aantal pakketten per seconde te zien, maar in het belastingsprofiel van deze servers ligt de gemiddelde pakketgrootte rond de 800 bytes, waardoor de waarden oplopen tot 1.5 Mpps. Zoals u kunt zien, heeft de server met nftables een enorme prestatiereserve. Momenteel verwerkt deze server snelheden tot 30 Gbit/s bij 3 Mpps en kan hij de fysieke netwerklimiet van 40 Gbps ruimschoots halen, terwijl er nog steeds voldoende CPU-bronnen beschikbaar zijn.
Ik hoop dat dit materiaal nuttig is voor netwerkengineers die de prestaties van hun servers willen verbeteren.
Bron: www.habr.com
