Бързо маршрутизиране и NAT в Linux

Тъй като IPv4 адресите се изчерпват, много телекомуникационни оператори са изправени пред необходимостта да осигурят на клиентите си мрежов достъп чрез преобразуване на адреси. В тази статия ще ви разкажа как можете да получите Carrier Grade NAT производителност на обикновени сървъри.

Малко история

Темата за изчерпването на IPv4 адресното пространство вече не е нова. В един момент в RIPE се появиха списъци с чакащи, след това се появиха борси, на които блокове от адреси се търгуват и се сключват сделки за отдаването им под наем. Постепенно телекомуникационните оператори започнаха да предоставят услуги за достъп до Интернет чрез превод на адреси и портове. Някои не успяха да получат достатъчно адреси, за да издадат „бял“ адрес на всеки абонат, докато други започнаха да спестяват пари, като отказват да купуват адреси на вторичния пазар. Производителите на мрежово оборудване подкрепиха тази идея, т.к тази функционалност обикновено изисква допълнителни модули за разширение или лицензи. Например, в линията MX рутери на Juniper (с изключение на най-новите MX104 и MX204), можете да изпълнявате NAPT на отделна сервизна карта MS-MIC, Cisco ASR1k изисква CGN лиценз, Cisco ASR9k изисква отделен A9K-ISM-100 модул и лиценз A9K-CGN -LIC към него. Като цяло удоволствието струва много пари.

IPTables

Задачата за изпълнение на NAT не изисква специализирани изчислителни ресурси, тя може да бъде решена от процесори с общо предназначение, които са инсталирани например във всеки домашен рутер. В мащаба на телекомуникационен оператор този проблем може да бъде решен с помощта на стокови сървъри, работещи с FreeBSD (ipfw/pf) или GNU/Linux (iptables). Няма да разглеждаме FreeBSD, защото... Спрях да използвам тази ОС преди доста време, така че ще се придържаме към GNU/Linux.

Активирането на превод на адреси не е никак трудно. Първо трябва да регистрирате правило в iptables в таблицата nat:

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

Операционната система ще зареди модула nf_conntrack, който ще следи всички активни връзки и ще извърши необходимите преобразувания. Тук има няколко тънкости. Първо, тъй като говорим за NAT в мащаба на телекомуникационен оператор, е необходимо да коригирате времето за изчакване, тъй като със стойности по подразбиране размерът на таблицата за превод бързо ще нарасне до катастрофални стойности. По-долу е даден пример за настройките, които използвах на моите сървъри:

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

И второ, тъй като размерът по подразбиране на таблицата за превод не е проектиран да работи в условията на телекомуникационен оператор, той трябва да бъде увеличен:

net.netfilter.nf_conntrack_max = 3145728

Също така е необходимо да се увеличи броят на кофи за хеш-таблицата, съхраняваща всички излъчвания (това е опция в модула nf_conntrack):

options nf_conntrack hashsize=1572864

След тези прости манипулации се получава напълно работещ дизайн, който може да преведе голям брой клиентски адреси в пул от външни. Ефективността на това решение обаче оставя много да се желае. При първите ми опити да използвам GNU/Linux за NAT (около 2013 г.), успях да постигна производителност от около 7Gbit/s при 0.8Mpps на сървър (Xeon E5-1650v2). Оттогава бяха направени много различни оптимизации в мрежовия стек на ядрото на GNU/Linux, производителността на един сървър на същия хардуер се увеличи до почти 18-19 Gbit/s при 1.8-1.9 Mpps (това бяха максималните стойности) , но търсенето на обем трафик, обработен от един сървър, нараства много по-бързо. В резултат на това бяха разработени схеми за балансиране на натоварването на различни сървъри, но всичко това увеличи сложността на настройката, поддръжката и поддържането на качеството на предоставяните услуги.

NFTтабли

В днешно време модерна тенденция в софтуерното „разместване на чантите“ е използването на DPDK и XDP. По тази тема са написани много статии, направени са много различни изказвания и се появяват търговски продукти (например SKAT от VasExperts). Но предвид ограничените програмни ресурси на телекомуникационните оператори, е доста проблематично да създадете сами какъвто и да е „продукт“, базиран на тези рамки. Ще бъде много по-трудно да се управлява такова решение в бъдеще; по-специално ще трябва да се разработят инструменти за диагностика. Например стандартният tcpdump с DPDK няма да работи просто така и няма да „вижда“ пакети, изпратени обратно към кабелите чрез XDP. На фона на всички разговори за нови технологии за извеждане на пренасочване на пакети към потребителското пространство, те останаха незабелязани доклади и статии Пабло Нейра Аюсо, поддържащ iptables, относно развитието на разтоварването на потока в nftables. Нека разгледаме този механизъм по-подробно.

Основната идея е, че ако маршрутизаторът е прекарал пакети от една сесия в двете посоки на потока (TCP сесията е преминала в състояние ESTABLISHED), тогава няма нужда да предавате следващите пакети от тази сесия през всички правила на защитната стена, тъй като всички тези проверки ще завършат с прехвърлянето на пакета по-нататък към маршрутизацията. И всъщност не е необходимо да избираме маршрут - вече знаем към кой интерфейс и към кой хост трябва да изпратим пакети в рамките на тази сесия. Всичко, което остава, е да съхраним тази информация и да я използваме за маршрутизиране на ранен етап от обработката на пакетите. При извършване на NAT е необходимо допълнително да се съхранява информация за промените в адресите и портовете, преведени от модула nf_conntrack. Да, разбира се, в този случай различни полицейски и други информационни и статистически правила в iptables спират да работят, но в рамките на задачата на отделен постоянен NAT или, например, граница, това не е толкова важно, защото услугите се разпределят между устройства.

Конфигурация

За да използваме тази функция се нуждаем от:

  • Използвайте ново ядро. Въпреки факта, че самата функционалност се появи в ядрото 4.16, доста дълго време беше много „сурова“ и редовно предизвикваше паника в ядрото. Всичко се стабилизира около декември 2019 г., когато бяха пуснати LTS ядра 4.19.90 и 5.4.5.
  • Пренапишете правилата на iptables във формат nftables, като използвате сравнително нова версия на nftables. Работи точно във версия 0.9.0

Ако всичко по принцип е ясно с първата точка, основното е да не забравяте да включите модула в конфигурацията по време на сглобяването (CONFIG_NFT_FLOW_OFFLOAD=m), тогава втората точка изисква обяснение. правилата на nftables са описани напълно различно от тези в iptables. Документация разкрива почти всички точки, има и специални конвертори правила от iptables към nftables. Затова ще дам само пример за настройка на NAT и разтоварване на потока. Малка легенда например: , - това са мрежовите интерфейси, през които преминава трафикът, в действителност те могат да бъдат повече от два. , — началния и крайния адрес на диапазона от „бели“ адреси.

NAT конфигурацията е много проста:

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

С разтоварването на потока е малко по-сложно, но съвсем разбираемо:

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

Това всъщност е цялата настройка. Сега целият TCP/UDP трафик ще попадне в таблицата fastnat и ще се обработва много по-бързо.

резултати

За да стане ясно колко „много по-бързо“ е това, ще прикача екранна снимка на натоварването на два реални сървъра, с един и същ хардуер (Xeon E5-1650v2), идентично конфигуриран, използващ същото Linux ядро, но изпълняващ NAT в iptables (NAT4) и в nftables (NAT5).

Бързо маршрутизиране и NAT в Linux

На екранната снимка няма графика на пакетите в секунда, но в профила на натоварване на тези сървъри средният размер на пакета е около 800 байта, така че стойностите достигат до 1.5Mpps. Както можете да видите, сървърът с nftables има огромен резерв за производителност. Понастоящем този сървър обработва до 30Gbit/s при 3Mpps и очевидно може да изпълни физическото мрежово ограничение от 40Gbps, като същевременно има безплатни ресурси на процесора.

Надявам се, че този материал ще бъде полезен за мрежовите инженери, които се опитват да подобрят производителността на своите сървъри.

Източник: www.habr.com

Добавяне на нов коментар