Обновлена 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