Rutare rapidă și NAT în Linux

Pe măsură ce adresele IPv4 se epuizează, mulți operatori de telecomunicații se confruntă cu nevoia de a oferi clienților lor acces la rețea folosind traducerea adreselor. În acest articol, vă voi spune cum puteți obține performanță NAT Carrier Grade pe serverele de mărfuri.

Un pic de istorie

Subiectul epuizării spațiului de adrese IPv4 nu mai este nou. La un moment dat, în RIPE au apărut liste de așteptare, apoi au apărut burse pe care au fost tranzacționate blocuri de adrese și s-au încheiat tranzacții pentru închirierea acestora. Treptat, operatorii de telecomunicații au început să ofere servicii de acces la Internet folosind traducerea adreselor și a porturilor. Unii nu au reușit să obțină suficiente adrese pentru a emite o adresă „albă” fiecărui abonat, în timp ce alții au început să facă economii refuzând să cumpere adrese de pe piața secundară. Producătorii de echipamente de rețea au susținut această idee, deoarece această funcționalitate necesită de obicei module de extensie sau licențe suplimentare. De exemplu, în linia de routere MX Juniper (cu excepția celor mai recente MX104 și MX204), puteți efectua NAPT pe un card de serviciu MS-MIC separat, Cisco ASR1k necesită o licență CGN, Cisco ASR9k necesită un modul separat A9K-ISM-100 și o licență A9K-CGN -LIC către el. În general, plăcerea costă o grămadă de bani.

iPTables

Sarcina de a efectua NAT nu necesită resurse de calcul specializate; poate fi rezolvată de procesoare de uz general, care sunt instalate, de exemplu, în orice router de acasă. La scara unui operator de telecomunicații, această problemă poate fi rezolvată folosind servere de mărfuri care rulează FreeBSD (ipfw/pf) sau GNU/Linux (iptables). Nu vom lua în considerare FreeBSD, deoarece... Am încetat să mai folosesc acest sistem de operare cu destul de mult timp în urmă, așa că vom rămâne la GNU/Linux.

Activarea traducerii adreselor nu este deloc dificilă. Mai întâi trebuie să înregistrați o regulă în iptables în tabelul nat:

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

Sistemul de operare va încărca modulul nf_conntrack, care va monitoriza toate conexiunile active și va efectua conversiile necesare. Există mai multe subtilități aici. În primul rând, deoarece vorbim despre NAT la scara unui operator de telecomunicații, este necesar să se ajusteze intervalele de timp, deoarece cu valorile implicite dimensiunea tabelului de traducere va crește rapid la valori catastrofale. Mai jos este un exemplu de setări pe care le-am folosit pe serverele mele:

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

Și în al doilea rând, deoarece dimensiunea implicită a tabelului de traducere nu este concepută pentru a funcționa în condițiile unui operator de telecomunicații, trebuie mărită:

net.netfilter.nf_conntrack_max = 3145728

De asemenea, este necesar să creșteți numărul de găleți pentru tabelul hash care stochează toate transmisiunile (aceasta este o opțiune în modulul nf_conntrack):

options nf_conntrack hashsize=1572864

După aceste manipulări simple, se obține un design complet funcțional care poate traduce un număr mare de adrese de clienți într-un pool de adrese externe. Cu toate acestea, performanța acestei soluții lasă mult de dorit. În primele mele încercări de a folosi GNU/Linux pentru NAT (circa 2013), am reușit să obțin performanțe de aproximativ 7 Gbit/s la 0.8 Mpps per server (Xeon E5-1650v2). De atunci, s-au făcut multe optimizări diferite în stiva de rețea kernel GNU/Linux, performanța unui server pe același hardware a crescut la aproape 18-19 Gbit/s la 1.8-1.9 Mpps (acestea au fost valorile maxime) , dar cererea de volum de trafic, procesat de un server, a crescut mult mai rapid. Drept urmare, s-au dezvoltat scheme de echilibrare a încărcăturii pe diferite servere, dar toate acestea au crescut complexitatea instalării, menținerii și menținerii calității serviciilor oferite.

NFTable

În zilele noastre, o tendință la modă în software-ul „saci de schimbare” este utilizarea DPDK și XDP. S-au scris o mulțime de articole pe această temă, s-au făcut multe discursuri diferite și apar produse comerciale (de exemplu, SKAT de la VasExperts). Dar având în vedere resursele limitate de programare ale operatorilor de telecomunicații, este destul de problematic să creați singur orice „produs” bazat pe aceste cadre. Va fi mult mai dificil să operați o astfel de soluție în viitor; în special, vor trebui dezvoltate instrumente de diagnosticare. De exemplu, tcpdump standard cu DPDK nu va funcționa așa și nu va „vedea” pachetele trimise înapoi la fire folosind XDP. În mijlocul tuturor discuțiilor despre noile tehnologii de transmitere a pachetelor în spațiul utilizatorului, au trecut neobservați rapoarte и articole Pablo Neira Ayuso, întreținător iptables, despre dezvoltarea fluxului de descărcare în nftables. Să ne uităm la acest mecanism mai detaliat.

Ideea principală este că, dacă routerul a transmis pachete dintr-o sesiune în ambele direcții ale fluxului (sesiunea TCP a intrat în starea ESTABLISHED), atunci nu este nevoie să treacă pachetele ulterioare ale acestei sesiuni prin toate regulile de firewall, deoarece toate aceste verificări se vor încheia în continuare cu transferul pachetului în continuare la rutare. Și nu trebuie de fapt să selectăm o rută - știm deja la ce interfață și la ce gazdă trebuie să trimitem pachete în această sesiune. Tot ce rămâne este să stocați aceste informații și să le folosiți pentru rutare într-un stadiu incipient al procesării pachetelor. Când efectuați NAT, este necesar să stocați suplimentar informații despre modificările adreselor și porturi traduse de modulul nf_conntrack. Da, desigur, în acest caz diverși polițiști și alte informații și reguli statistice din iptables nu mai funcționează, dar în cadrul sarcinii unui NAT permanent separat sau, de exemplu, a unei granițe, acest lucru nu este atât de important, deoarece serviciile sunt distribuite pe dispozitive.

configurație

Pentru a folosi această funcție avem nevoie de:

  • Utilizați un miez proaspăt. În ciuda faptului că funcționalitatea în sine a apărut în nucleul 4.16, pentru o perioadă destul de lungă a fost foarte „brută” și a provocat în mod regulat panică în nucleu. Totul s-a stabilizat în jurul lunii decembrie 2019, când au fost lansate nucleele LTS 4.19.90 și 5.4.5.
  • Rescrie regulile iptables în format nftables folosind o versiune destul de recentă a nftables. Funcționează exact în versiunea 0.9.0

Dacă totul este clar în principiu cu primul punct, principalul lucru este să nu uitați să includeți modulul în configurație în timpul asamblarii (CONFIG_NFT_FLOW_OFFLOAD=m), atunci al doilea punct necesită explicații. regulile nftables sunt descrise complet diferit decât în ​​iptables. Documentație dezvăluie aproape toate punctele, există și speciale convertoare reguli de la iptables la nftables. Prin urmare, voi da doar un exemplu de configurare a NAT și a descărcarii fluxului. O mică legendă, de exemplu: , - acestea sunt interfețele de rețea prin care trece traficul; în realitate pot fi mai mult de două. , — adresa de început și de sfârșit a intervalului de adrese „albe”.

Configurarea NAT este foarte simplă:

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

Cu descărcarea fluxului, este puțin mai complicat, dar destul de ușor de înțeles:

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

Asta, de fapt, este întreaga configurație. Acum tot traficul TCP/UDP va intra în tabelul fastnat și va fi procesat mult mai rapid.

Constatări

Pentru a clarifica cât de „mult mai rapid” este aceasta, voi atașa o captură de ecran a încărcării pe două servere reale, cu același hardware (Xeon E5-1650v2), configurat identic, folosind același nucleu Linux, dar efectuând NAT în iptables (NAT4) și în nftables (NAT5).

Rutare rapidă și NAT în Linux

Nu există un grafic al pachetelor pe secundă în captură de ecran, dar în profilul de încărcare al acestor servere dimensiunea medie a pachetului este de aproximativ 800 de octeți, astfel încât valorile ajung până la 1.5Mpps. După cum puteți vedea, serverul cu nftables are o rezervă uriașă de performanță. În prezent, acest server procesează până la 30Gbit/s la 3Mpps și este în mod clar capabil să îndeplinească limitarea fizică a rețelei de 40Gbps, având în același timp resurse CPU libere.

Sper că acest material va fi util inginerilor de rețea care încearcă să îmbunătățească performanța serverelor lor.

Sursa: www.habr.com

Adauga un comentariu