Routing veloce e NAT in Linux

Con l'esaurirsi degli indirizzi IPv4, molti operatori di telecomunicazioni si sono trovati nella necessità di organizzare l'accesso alla rete dei propri clienti attraverso la traduzione degli indirizzi. In questo articolo spiegherò come ottenere prestazioni di livello Carrier Grade NAT su server commodity.

Un po' di storia

Il tema dell'esaurimento dello spazio degli indirizzi IPv4 non è nuovo. A un certo punto, in RIPE, sono apparse liste d'attesa, poi sono nate borse in cui si scambiavano blocchi di indirizzi e si stipulavano contratti di affitto. Gradualmente, gli operatori di telecomunicazioni hanno iniziato a fornire servizi di accesso a Internet attraverso la traduzione di indirizzi e porte. Alcuni non sono riusciti a ottenere un numero sufficiente di indirizzi per assegnare un indirizzo 'pubblico' a ciascun utente, mentre altri hanno iniziato a risparmiare denaro rifiutandosi di acquistare indirizzi sul mercato secondario. I produttori di apparecchiature di rete hanno sostenuto questa idea, poiché questa funzionalità richiede solitamente moduli di espansione o licenze aggiuntive. Ad esempio, per Juniper nella serie di router MX (eccetto i più recenti MX104 e MX204), eseguire NAPT è possibile su una scheda di servizio separata MS-MIC, su Cisco ASR1k è necessaria la licenza CGN, mentre su Cisco ASR9k è necessario un modulo separato A9K-ISM-100 e la licenza A9K-CGN-LIC. In generale, il divertimento ha un costo considerevole.

IPTables

Il compito di eseguire NAT non richiede risorse computazionali specializzate; può essere gestito da processori generici, come quelli installati in qualsiasi router domestico. A livello di un operatore di telecomunicazioni, questo compito può essere affrontato utilizzando server commodity con FreeBSD (ipfw/pf) o GNU/Linux (iptables). Non considereremo FreeBSD, dato che ho smesso di usare questo sistema operativo da un bel po', quindi ci concentreremo su GNU/Linux.

Abilitare la traduzione degli indirizzi non è affatto complicato. Per iniziare, è necessario scrivere una regola in iptables nella tabella nat:

iptables -t nat -A POSTROUTING -s 100.64.0.0/10 -j SNAT --to - --persistent

Il sistema operativo caricherà il modulo nf_conntrack, che monitorerà tutte le connessioni attive ed eseguirà le necessarie trasformazioni. Ci sono alcune sottigliezze. In primo luogo, poiché si tratta di NAT su scala dell'operatore di comunicazione, è necessario regolare i timeout, poiché con i valori predefiniti la dimensione della tabella di traduzione crescerà abbastanza rapidamente a valori catastrofici. Di seguito un esempio di 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é per impostazione predefinita la dimensione della tabella di traduzione non è progettata per l'uso da parte di un operatore di comunicazione, deve essere aumentata:

net.netfilter.nf_conntrack_max = 3145728

È anche necessario aumentare il numero di buckets per la tabella hash che memorizza tutte le traduzioni (questa è un'opzione del modulo nf_conntrack):

options nf_conntrack hashsize=1572864

Dopo queste semplici manipolazioni, si ottiene una configurazione perfettamente funzionante in grado di tradurre un gran numero di indirizzi client in un pool di indirizzi esterni. Tuttavia, le prestazioni di questa soluzione lasciano a desiderare. Nei miei primi tentativi di utilizzare GNU/Linux per NAT (circa nel 2013), sono riuscito a raggiungere prestazioni di circa 7Gbit/s con 0.8Mpps su un server (Xeon E5-1650v2). Da allora, nel stack di rete del kernel GNU/Linux sono state implementate molte diverse ottimizzazioni, e le prestazioni di un singolo server con la stessa configurazione sono praticamente aumentate fino a 18-19 Gbit/s con 1.8-1.9 Mpps (questi erano i valori massimi), ma la domanda sul volume del traffico trattato da un singolo server è cresciuta molto più rapidamente. Di conseguenza, sono stati sviluppati schemi di bilanciamento del carico su più server, ma tutto ciò ha aumentato la complessità della configurazione, della manutenzione e del mantenimento della qualità dei servizi erogati.

NFTables

Attualmente, la tendenza nel 'riassegnare pacchetti' nei programmi è l'uso di DPDK e XDP. Sono stati scritti numerosi articoli su questo argomento, ci sono state molte presentazioni diverse e sono nati prodotti commerciali (ad esempio, SKAT di VasExperts). Tuttavia, nel contesto delle risorse limitate dei programmatori nei fornitori di servizi di telecomunicazione, sviluppare autonomamente un 'oggetto' basato su questi framework è piuttosto problematico. Sfruttare tale soluzione in seguito sarà molto più difficile, in particolare, sarà necessario sviluppare strumenti di diagnostica. Ad esempio, il classico tcpdump con DPDK non funzionerà semplicemente, e nemmeno i pacchetti inviati di nuovo nei cavi tramite XDP saranno 'visibili'. Tra tutte le chiacchiere sulle nuove tecnologie di invio del forwarding dei pacchetti nello user-space, sono passati inosservati relazioni e sul CommunityHelpWiki (sullo stesso dominio Pablo Neira Ayuso, mantenitore di iptables, sulla progettazione del flow offloading in nftables. Esaminiamo questo meccanismo più nel dettaglio.

L'idea principale è che se il router ha fatto passare pacchetti di una sessione in entrambe le direzioni del flusso (la sessione TCP è passata allo stato ESTABLISHED), non è necessario far passare i pacchetti successivi di quella sessione attraverso tutte le regole del firewall, poiché tutti questi controlli si concluderanno comunque con la trasmissione del pacchetto nel routing. Inoltre, non c'è bisogno di eseguire anche la scelta del percorso — sappiamo già in quale interfaccia e a quale host inviare i pacchetti all'interno di questa sessione. Rimane solo da salvare queste informazioni e utilizzarle per il routing nelle fasi iniziali del trattamento del pacchetto. Durante l'esecuzione del NAT, è necessario conservare ulteriormente le informazioni sulle modifiche degli indirizzi e delle porte, trasformate dal modulo nf_conntrack. Certo, in questo caso smettono di funzionare vari polizieri e altre regole statistiche informative in iptables, ma nel contesto della funzione di un NAT singolo o, ad esempio, di un bordo, non è così rilevante, perché i servizi sono distribuiti su più dispositivi.

Configurazione

Per utilizzare questa funzione dobbiamo:

  • Utilizzare un kernel recente. Sebbene la funzionalità stessa sia apparsa già nel kernel 4.16, è rimasta per molto tempo piuttosto 'grezza' e ha regolarmente causato panic del kernel. Tutto si è stabilizzato circa a dicembre 2019, quando sono stati rilasciati i kernel LTS 4.19.90 e 5.4.5.
  • Riscrivere le regole di iptables nel formato nftables, utilizzando una versione abbastanza recente di nftables. Funziona perfettamente nella versione 0.9.0

Se il primo punto è in linea di massima chiaro, l'importante è non dimenticare di includere il modulo nella configurazione durante la compilazione (CONFIG_NFT_FLOW_OFFLOAD=m), il secondo punto richiede delle spiegazioni. Le regole di nftables sono descritte in modo completamente diverso rispetto a iptables. Documentazione copre praticamente tutti gli aspetti, ci sono anche convertitori di regole da iptables a nftables. Pertanto, fornirò solo un esempio di configurazione NAT e flow offload. Una piccola leggenda per l'esempio: , — sono le interfacce di rete attraverso cui passa il traffico, in realtà possono essercene più di due. , — l'indirizzo iniziale e finale dell'intervallo di indirizzi "pubblici". La configurazione NAT è molto semplice:

Con flow offload è un po' più complicato, ma del tutto comprensibile:

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

Ecco, insomma, tutta la configurazione. Ora tutto il traffico TCP/UDP verrà instradato nella tabella fastnat e elaborato molto più rapidamente.

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

Per comprendere quanto sia "molto più veloce", allegherò uno screenshot del carico su due server reali, con la stessa configurazione (Xeon E5-1650v2), impostati in modo identico, utilizzando lo stesso kernel Linux, ma che eseguono NAT in iptables (NAT4) e in nftables (NAT5).

Risultati

Nello screenshot non c'è il grafico dei pacchetti al secondo, ma nel profilo di carico di questi server la dimensione media del pacchetto è di circa 800 byte, quindi i valori arrivano fino a 1.5Mpps. Come si può vedere, la capacità di prestazione del server con nftables è enorme. In questo momento, questo server gestisce fino a 30Gbit/s a 3Mpps e ha chiaramente il potenziale per raggiungere il limite fisico della rete di 40Gbps, avendo al contempo risorse CPU disponibili.

Routing veloce e NAT in Linux

Spero che questo materiale sia utile agli ingegneri di rete che cercano di migliorare le prestazioni dei loro server.

Con l'esaurimento degli indirizzi IPv4, molti operatori di telecomunicazioni si sono trovati a dover organizzare l'accesso dei propri clienti alla rete tramite la traduzione degli indirizzi.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster