Routing veloce e NAT in Linux

Con l'esaurimento degli indirizzi IPv4, molti operatori di telecomunicazioni si trovano ad affrontare la necessità di fornire ai propri clienti l'accesso alla rete utilizzando la traduzione degli indirizzi. In questo articolo ti spiegherò come ottenere prestazioni NAT di livello Carrier sui server di base.

Un po 'di storia

Il tema dell'esaurimento dello spazio degli indirizzi IPv4 non è più nuovo. Ad un certo punto, nel RIPE sono apparse le liste d'attesa, poi sono emersi gli scambi sui quali sono stati scambiati blocchi di indirizzi e sono stati conclusi accordi per affittarli. A poco a poco, gli operatori di telecomunicazioni hanno iniziato a fornire servizi di accesso a Internet utilizzando la traduzione di indirizzi e porte. Alcuni non sono riusciti a ottenere abbastanza indirizzi per fornire un indirizzo “bianco” a ciascun abbonato, mentre altri hanno iniziato a risparmiare denaro rifiutandosi di acquistare indirizzi sul mercato secondario. I produttori di apparecchiature di rete hanno sostenuto questa idea perché questa funzionalità richiede solitamente moduli di estensione o licenze aggiuntive. Ad esempio, nella linea di router MX di Juniper (ad eccezione degli ultimi MX104 e MX204), è possibile eseguire NAPT su una scheda di servizio MS-MIC separata, Cisco ASR1k richiede una licenza CGN, Cisco ASR9k richiede un modulo A9K-ISM-100 separato e una licenza A9K-CGN -LIC per lui. In generale, il piacere costa un sacco di soldi.

IPTables

Il compito di eseguire NAT non richiede risorse informatiche specializzate; può essere risolto da processori generici, installati, ad esempio, in qualsiasi router domestico. Su scala di un operatore di telecomunicazioni, questo problema può essere risolto utilizzando server commerciali che eseguono FreeBSD (ipfw/pf) o GNU/Linux (iptables). Non prenderemo in considerazione FreeBSD, perché... Ho smesso di usare questo sistema operativo parecchio tempo fa, quindi resteremo fedeli a GNU/Linux.

Abilitare la traduzione degli indirizzi non è affatto difficile. Per prima cosa devi registrare una regola in iptables nella tabella nat:

iptables -t nat -A POSTROUTING -s 100.64.0.0/10 -j SNAT --to <pool_start_addr>-<pool_end_addr> --persistent

Il sistema operativo caricherà il modulo nf_conntrack, che monitorerà tutte le connessioni attive ed eseguirà le conversioni necessarie. Ci sono diverse sottigliezze qui. In primo luogo, poiché stiamo parlando di NAT sulla scala di un operatore di telecomunicazioni, è necessario regolare i timeout, poiché con i valori predefiniti la dimensione della tabella di traduzione raggiungerà rapidamente valori catastrofici. Di seguito è riportato un esempio delle impostazioni che ho utilizzato sui miei server:

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

In secondo luogo, poiché la dimensione predefinita della tabella di traduzione non è progettata per funzionare nelle condizioni di un operatore di telecomunicazioni, è necessario aumentarla:

net.netfilter.nf_conntrack_max = 3145728

È inoltre necessario aumentare il numero di bucket per la tabella hash che memorizza tutte le trasmissioni (questa è un'opzione nel modulo nf_conntrack):

options nf_conntrack hashsize=1572864

Dopo queste semplici manipolazioni si ottiene un progetto completamente funzionante in grado di tradurre un gran numero di indirizzi client in un pool di indirizzi esterni. Tuttavia, le prestazioni di questa soluzione lasciano molto a desiderare. Nei miei primi tentativi di utilizzo di GNU/Linux per NAT (intorno al 2013), sono riuscito a ottenere prestazioni di circa 7 Gbit/s a 0.8 Mpps per server (Xeon E5-1650v2). Da allora sono state apportate numerose ottimizzazioni allo stack di rete del kernel GNU/Linux, le prestazioni di un server sullo stesso hardware sono aumentate fino a quasi 18-19 Gbit/s a 1.8-1.9 Mpps (questi erano i valori massimi) , ma la domanda di volume di traffico elaborato da un server è cresciuta molto più velocemente. Di conseguenza, sono stati sviluppati schemi per bilanciare il carico su server diversi, ma tutto ciò ha aumentato la complessità di installazione, mantenimento e mantenimento della qualità dei servizi forniti.

NTFables

Al giorno d'oggi, una tendenza alla moda nel software "shifting bags" è l'uso di DPDK e XDP. Sono stati scritti molti articoli su questo argomento, sono stati fatti molti discorsi diversi e compaiono prodotti commerciali (ad esempio SKAT di VasExperts). Ma date le limitate risorse di programmazione degli operatori di telecomunicazioni, è piuttosto problematico creare da soli qualsiasi "prodotto" basato su questi framework. In futuro sarà molto più difficile mettere in atto una soluzione del genere; in particolare, dovranno essere sviluppati strumenti diagnostici. Ad esempio, il tcpdump standard con DPDK non funzionerà proprio così e non “vedrà” i pacchetti rispediti ai cavi utilizzando XDP. In mezzo a tutti i discorsi sulle nuove tecnologie per l'output dell'inoltro dei pacchetti nello spazio utente, sono passati inosservati rapporti и articoli Pablo Neira Ayuso, manutentore di iptables, sullo sviluppo del flusso di scarico in nftables. Vediamo questo meccanismo più in dettaglio.

L'idea principale è che se il router passa i pacchetti da una sessione in entrambe le direzioni del flusso (la sessione TCP è entrata nello stato ESTABLISHED), non è necessario passare i pacchetti successivi di questa sessione attraverso tutte le regole del firewall, perché tutti questi controlli si concluderanno comunque con il trasferimento del pacchetto ulteriormente al routing. E in realtà non abbiamo bisogno di selezionare un percorso: sappiamo già a quale interfaccia e a quale host dobbiamo inviare i pacchetti all'interno di questa sessione. Tutto ciò che resta da fare è memorizzare queste informazioni e utilizzarle per l'instradamento in una fase iniziale dell'elaborazione dei pacchetti. Quando si esegue NAT, è necessario memorizzare inoltre le informazioni sulle modifiche agli indirizzi e alle porte tradotte dal modulo nf_conntrack. Sì, certo, in questo caso vari poliziotti e altre informazioni e regole statistiche in iptables smettono di funzionare, ma nell'ambito del compito di un NAT permanente separato o, ad esempio, di un confine, questo non è così importante, perché i servizi sono distribuiti su tutti i dispositivi.

Configurazione

Per utilizzare questa funzione abbiamo bisogno di:

  • Usa un nocciolo fresco. Nonostante il fatto che la funzionalità stessa sia apparsa nel kernel 4.16, per molto tempo è stata molto "grezza" e causava regolarmente il panico del kernel. Tutto si è stabilizzato intorno a dicembre 2019, quando sono stati rilasciati i kernel LTS 4.19.90 e 5.4.5.
  • Riscrivi le regole di iptables nel formato nftables utilizzando una versione abbastanza recente di nftables. Funziona esattamente nella versione 0.9.0

Se in linea di principio tutto è chiaro con il primo punto, l'importante è non dimenticare di includere il modulo nella configurazione durante l'assemblaggio (CONFIG_NFT_FLOW_OFFLOAD=m), quindi il secondo punto richiede una spiegazione. Le regole di nftables sono descritte in modo completamente diverso rispetto a iptables. Documentazione rivela quasi tutti i punti, ci sono anche speciali convertitori regole da iptables a nftables. Pertanto, fornirò solo un esempio di configurazione del NAT e dell'offload del flusso. Una piccola leggenda ad esempio: , - queste sono le interfacce di rete attraverso le quali passa il traffico; in realtà possono essercene più di due. , — l'indirizzo iniziale e finale dell'intervallo di indirizzi “bianchi”.

La configurazione del NAT è molto semplice:

#! /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
        }
}

Con l'offload del flusso è un po' più complicato, ma abbastanza comprensibile:

#! /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;
        }
}

Questo, in effetti, è l'intero setup. Ora tutto il traffico TCP/UDP cadrà nella tabella fastnat e verrà elaborato molto più velocemente.

Giudizio

Per rendere chiaro quanto questo sia “molto più veloce”, allegherò uno screenshot del caricamento su due real server, con lo stesso hardware (Xeon E5-1650v2), configurato in modo identico, utilizzando lo stesso kernel Linux, ma eseguendo NAT in iptables (NAT4) e in nftables (NAT5).

Routing veloce e NAT in Linux

Nello screenshot non è presente il grafico dei pacchetti al secondo, ma nel profilo di carico di questi server la dimensione media dei pacchetti è di circa 800 byte, quindi i valori arrivano fino a 1.5Mpps. Come puoi vedere, il server con nftables ha un'enorme riserva di prestazioni. Attualmente, questo server elabora fino a 30 Gbit/s a 3 Mpps ed è chiaramente in grado di soddisfare il limite della rete fisica di 40 Gbps, pur disponendo di risorse CPU gratuite.

Spero che questo materiale possa essere utile agli ingegneri di rete che cercano di migliorare le prestazioni dei propri server.

Fonte: habr.com

Aggiungi un commento