Тюнингуем ipnat/ipfilter для большой нагрузки

Обновлена 4.10.2016

С недавнего времени начались проблемы с инетом. Причём после шлюза всё нормально пингуется, а в локалке идут страшные потери пакетов, страницы недогружаются. Полазив в инете, понял, что проблема может крыться в переполнении таблиц NAT‘а. А если быть точнее, то в переполнении таблиц ipnat’a. Именно его я использую для трансляции адресов.

Для начала проверим состояния ipnat’a.

#ipnat -s

mapped  in      86303767        out     3841584134
added   104357220       expired 0
no memory       905283  bad nat 2172013
inuse   29996
rules   18
wilds   2

Заодно посмотрим топ качков (слева колличество открытых соединений):

#ipnat -l | awk '{print $2}'| sort | uniq -c | sort -r | grep  "10.0." | less

3185 10.0.19.70
2238 10.0.19.94
1807 10.0.19.80
1701 10.0.3.111
973 10.0.19.116
744 10.0.19.68
672 10.0.3.83
663 10.0.19.75
654 10.0.19.136
590 10.0.19.38
526 10.0.3.230
412 10.0.19.91
399 10.0.19.120
363 10.0.19.83
272 10.0.19.39
263 10.0.19.92
240 10.0.19.56
208 10.0.19.72
188 10.0.19.6

Теперь посмотрим на максимальное значение

#ipf -T list

fr_flags        min 0   max 0xffffffff  current 0
fr_active       min 0   max 0   current 0
fr_control_forwarding   min 0   max 0x1 current 0
fr_update_ipid  min 0   max 0x1 current 0
fr_chksrc       min 0   max 0x1 current 0
fr_pass min 0   max 0xffffffff  current 134217730
fr_tcpidletimeout       min 0x1 max 0x7fffffff  current 864000
fr_tcpclosewait min 0x1 max 0x7fffffff  current 480
fr_tcplastack   min 0x1 max 0x7fffffff  current 480
fr_tcptimeout   min 0x1 max 0x7fffffff  current 480
fr_tcpclosed    min 0x1 max 0x7fffffff  current 120
fr_tcphalfclosed        min 0x1 max 0x7fffffff  current 14400
fr_udptimeout   min 0x1 max 0x7fffffff  current 240
fr_udpacktimeout        min 0x1 max 0x7fffffff  current 24
fr_icmptimeout  min 0x1 max 0x7fffffff  current 120
fr_icmpacktimeout       min 0x1 max 0x7fffffff  current 12
fr_iptimeout    min 0x1 max 0x7fffffff  current 120
fr_statemax     min 0x1 max 0x7fffffff  current 4013
fr_statesize    min 0x1 max 0x7fffffff  current 5737
fr_state_lock   min 0   max 0x1 current 0
fr_state_maxbucket      min 0x1 max 0x7fffffff  current 26
fr_state_maxbucket_reset        min 0   max 0x1 current 1
ipstate_logging min 0   max 0x1 current 0
fr_nat_lock     min 0   max 0x1 current 0
ipf_nattable_sz min 0x1 max 0x7fffffff  current 2047
ipf_nattable_max        min 0x1 max 0x7fffffff  current 30000
ipf_natrules_sz min 0x1 max 0x7fffffff  current 127
ipf_rdrrules_sz min 0x1 max 0x7fffffff  current 127
ipf_hostmap_sz  min 0x1 max 0x7fffffff  current 2047
fr_nat_maxbucket        min 0x1 max 0x7fffffff  current 22
fr_nat_maxbucket_reset  min 0   max 0x1 current 1
nat_logging     min 0   max 0x1 current 0
fr_defnatage    min 0x1 max 0x7fffffff  current 1200
fr_defnatipage  min 0x1 max 0x7fffffff  current 120
fr_defnaticmpage        min 0x1 max 0x7fffffff  current 6
ipfr_size       min 0x1 max 0x7fffffff  current 257
fr_ipfrttl      min 0x1 max 0x7fffffff  current 120
ippr_ftp_debug  min 0   max 0xa current 0

Ниже будут приведены решения отдельно для FreeBSD и отдельно для Solaris

FreeBSD

Мда… До переполнения осталось 4 записи. Ну что ж, будем тюнинговать.

Есть 2 способа: пересобрать ipnat и применять параметры налету (если пересобрать ядро не представляется возможным)

Для второго варианта достаточно добавить строку в /etc/rc.conf и перезапустить ipfilter/ipnat

ipfilter_flags="-D -T ipf_nattable_sz=10009,ipf_nattable_max=300000 -E"

Первый способ описан ниже.
Перед этим обязательно прочесть заметку в конце статьи «А что же мы получаем, включая LARGE_NAT». Открываем файл /usr/src/sys/contrib/ipfilter/netinet/ip_nat.h и ищем словосочетание #undefine LARGE_NAT(#undef LARGE_NAT) и меняем на #define LARGE_NAT. Этим мы включили «большое нат», то есть намного увеличили значение (теперь оно будет 180000, после пересборки ядра, конечно же 🙂 ). Но мне показалось и этого мало :), мало ли, потом опять будет что-то. Поэтому я реши немного изменить некоторые значения в этом файле (первая строка, что будет при включённом «большом нате», вторая — на что изменил)

// #  define    NAT_SIZE        2047
#  define    NAT_SIZE        4093

// #  define    RDR_SIZE        2047
#  define    RDR_SIZE        4093

// #  define    HOSTMAP_SIZE    8191
#  define    HOSTMAP_SIZE    16383

// #  define    NAT_TABLE_MAX   180000
#  define    NAT_TABLE_MAX   300000

// #  define    NAT_TABLE_SZ    16383
#  define       NAT_TABLE_SZ    32765

Как видим, почти в 2 раза увеличил.

Теперь пересобираем ядро и наслаждаемся новыми переменными. (Как пересобрать ядро читаем здесь).

После пересборки смотрим наши переменные

#ipf -T list

fr_flags        min 0   max 0xffffffff  current 0
fr_active       min 0   max 0   current 0
fr_control_forwarding   min 0   max 0x1 current 0
fr_update_ipid  min 0   max 0x1 current 0
fr_chksrc       min 0   max 0x1 current 0
fr_minttl       min 0   max 0x1 current 4
fr_icmpminfragmtu       min 0   max 0x1 current 68
fr_pass min 0   max 0xffffffff  current 134217730
fr_tcpidletimeout       min 0x1 max 0x7fffffff  current 864000
fr_tcpclosewait min 0x1 max 0x7fffffff  current 480
fr_tcplastack   min 0x1 max 0x7fffffff  current 60
fr_tcptimeout   min 0x1 max 0x7fffffff  current 480
fr_tcpclosed    min 0x1 max 0x7fffffff  current 60
fr_tcphalfclosed        min 0x1 max 0x7fffffff  current 14400
fr_udptimeout   min 0x1 max 0x7fffffff  current 240
fr_udpacktimeout        min 0x1 max 0x7fffffff  current 24
fr_icmptimeout  min 0x1 max 0x7fffffff  current 120
fr_icmpacktimeout       min 0x1 max 0x7fffffff  current 12
fr_iptimeout    min 0x1 max 0x7fffffff  current 120
fr_statemax     min 0x1 max 0x7fffffff  current 4013
fr_statesize    min 0x1 max 0x7fffffff  current 5737
fr_state_lock   min 0   max 0x1 current 0
fr_state_maxbucket      min 0x1 max 0x7fffffff  current 26
fr_state_maxbucket_reset        min 0   max 0x1 current 1
ipstate_logging min 0   max 0x1 current 1
fr_nat_lock     min 0   max 0x1 current 0
ipf_nattable_sz min 0x1 max 0x7fffffff  current 32765
ipf_nattable_max        min 0x1 max 0x7fffffff  current 300000
ipf_natrules_sz min 0x1 max 0x7fffffff  current 4093
ipf_rdrrules_sz min 0x1 max 0x7fffffff  current 4093
ipf_hostmap_sz  min 0x1 max 0x7fffffff  current 16383
fr_nat_maxbucket        min 0x1 max 0x7fffffff  current 30
fr_nat_maxbucket_reset  min 0   max 0x1 current 1
nat_logging     min 0   max 0x1 current 1
fr_defnatage    min 0x1 max 0x7fffffff  current 1200
fr_defnatipage  min 0x1 max 0x7fffffff  current 120
fr_defnaticmpage        min 0x1 max 0x7fffffff  current 6
fr_nat_doflush  min 0   max 0x1 current 0
ipf_proxy_debug min 0   max 0xa current 0
ipfr_size       min 0x1 max 0x7fffffff  current 257
fr_ipfrttl      min 0x1 max 0x7fffffff  current 120
ipl_suppress    min 0   max 0x1 current 1
ipl_logmax      min 0   max 0x7fffffff  current 7
ipl_logall      min 0   max 0x1 current 0
ipl_logsize     min 0   max 0x80000     current 8192
ippr_ftp_debug  min 0   max 0xa current 0

Видим, что так же изменились некоторые другие переменные, которые мы тюнинговали.

Заметка: А что же мы получаем, включая LARGE_NAT?

Когда я заюзал на новом сервачке LARGE_NAT, то столкнулся с некоторой проблемой: рвуться сессии NAT’a, если они неактивны 10 минут. Хотя, если посмотреть на вывод ipf -T list, то там указано 864000 (то есть неделя). В чём же может быть дело? А дело, как выяснилось, именно в определении LARGE_NAT. Если посмотреть внимательно код в файле ip_nat.c, то можно увидеть, что время обрыва сессии принудительно устанавливается в 10 минут, если включён LARGE_NAT. Меня такое не устравало и я пошёл искать в инете инфу по этому поводу. На одном из форумов кинули линк, где шла (год был 2003) переписка по этому поводу. Почитав внимательно, и понял, что LARGE_NAT используеся, когда мы имеем тысячи подсетей (а у меня их не больше 30). Да и насколько я понял, особый смысл в этом параметре именно ограничение сессии в 10 минут, посколько для тысяч сетей колличество одновременно открытых NAT-сессий может достигать миллионов. Соотвественно, таблица NAT’a быстро бы переполнялась. Для себя же я решил: не использовать LARGE_NAT, а просто увеличить те значения, которые мне нужно, чего и вам советую. Сервак с таким конфигом работает уже несколько дней, нареканий нет, обслуживает несколько сотен юзеров.

Поэтому советую, не использовать LARGE_NAT, а просто изменить нужные значения:

// #  define    NAT_SIZE        127
#  define    NAT_SIZE        4093

// #  define    RDR_SIZE        127
#  define    RDR_SIZE        4093

// #  define    HOSTMAP_SIZE    2047
#  define    HOSTMAP_SIZE    16383

// #  define    NAT_TABLE_MAX   30000
#  define    NAT_TABLE_MAX   300000

// #  define    NAT_TABLE_SZ    2047
#  define       NAT_TABLE_SZ    32765

Solaris

Тестовый стенд: SunOS 5.11 11.1 sun4v sparc SUNW,Sun-Fire-T1000

В Solaris пересобирать ipf не было никакого желания. Решение простое: изменить параметры налету при при запуске.  Для этого добавим пару строк в запускаемый манифестом скрипт (/lib/svc/method/ipfilter). В самое начало раздела

case "$1" in
        start)

добавить такое

ipf -D > /dev/null
ipf -T ipf_nattable_sz=10009,ipf_nattable_max=300000,fr_tcpidletimeout=172800 > /dev/null
ipf -E > /dev/null

После этого просто перезапустить службу svc:/network/ipfilter:default

Но правильно нужно делать так: в global-зоне пишем все параметры в файл /usr/kernel/drv/ipf.conf:

name="ipf" parent="pseudo" instance=0 fr_statemax=113279 fr_statesize=151007;

Для того, что бы изменения подхватились, нужно:

— везде (в global и остальных зонах) выгрузить модуль ipf (остановить сервис ipfilter)
— в global-зоне обновить конфиг драйвера:

# update_drv -v ipf
ipf.conf updated in the kernel.

— после этого включить сервис ipfilter

 

Оригинал статьи здесь.

Опубликовано с разрешения редакции журнала RootUA и газеты FOSS News

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *