25 ноября 2010 в 13:08

Повышение производительности netfilter, использование ipset

iptables — интерфейс к файрволу Linux (netfilter). При большом количестве правил iptables нагрузка может быть достаточно высокой и создавать проблемы. В этой заметке я постараюсь описать, что влияет на производительность iptables и как ее повысить.

Основная причина нагрузки


Для начала: из-за чего вообще возникает нагрузка при большом количестве правил? Из-за того, что каждому сетевому пакету, обрабатываемому ядром, приходится проходить сравнение по всему списку правил каждой цепочки. Приведу схему, которая отражает цепочки netfilter и порядок прохождения по ним пакетов:



На схеме видно через какие цепочки пройдет тот или иной пакет и можно оценить нагрузку. Возьмем одно простейшее правило:

-A INPUT -s 10.1.1.1/32 -p tcp --dport 25 -j DROP

Это правило указывает файрволлу отбрасывать все пакеты с IP-адреса 10.1.1.1 на порт 25 локальной машины. Таблицу (-t) я не указываю, поэтому будет использоваться дефолтная (-t filter), что нам и нужно. Понятно, что действие DROP выполняется далеко не всегда. Однако, сравнение IP-адреса источника будет проходить любой входящий на локальную машину пакет. А если таких правил тысяча?

Что делать?


Теперь подумаем, как можно ускорить работу файрволла. Первый ответ очевиден — сокращать количество правил. Но вряд ли удастся серьезно оптимизировать записи, особенно если они изначально составлялись грамотно. Где-то вместо правил на несколько IP-адресов можно написать одно правило на подсеть. Где-то вместо правил на несколько портов можно использовать -m multiport и --dports/--sports a,b,c (a,b,c — номера портов). И так далее.

Ветвление правил, дополнительные цепочки


А есть еще второй вариант — использование ветвлений правил. Суть его в том, что однотипные правила группируются в отдельную цепочку, а в основной цепочке остается одно правило, которое перенаправляет пакет в отдельную цепочку в зависимости от какого-то общего признака пакетов. Приведу пример. Имеются три правила:

-A INPUT -s 10.1.1.1/32 -p icmp -j ACCEPT
-A INPUT -s 10.1.1.2/32 -p icmp -j ACCEPT
-A INPUT -s 10.1.1.3/32 -p icmp -j ACCEPT


Что объединяет эти правила? Одинаковый протокол — icmp. Вот по этому признаку и стоит сгруппировать правила. Вообще в данном случае эффективнее использовать ipset, но об этом дальше. Для группировки необходимо создать новую цепочку, а затем создать правило, которое будет отправлять пакеты в эту цепочку.
Создать цепочку PROT_ICMP:

iptables -N PROT_ICMP

Определить правила в этой цепочке:

-A PROT_ICMP -s 10.1.1.1/32 -j ACCEPT
-A PROT_ICMP -s 10.1.1.2/32 -j ACCEPT
-A PROT_ICMP -s 10.1.1.3/32 -j ACCEPT


Как видите, протокол (-p) в этой цепочке мы уже не проверяем, т.к. отправлять в эту цепочку будем только ICMP пакеты. И наконец отправить все пакеты ICMP в эту цепочку:

-A INPUT -p icmp -g PROT_ICMP

Теперь входящий пакет, если он не ICMP, будет проходить лишь через одно правило вместо трех.

ipset


А теперь предположим следующую ситуацию:

-A INPUT -s 10.1.1.1/32 -j DROP
-A INPUT -s 10.1.1.2/32 -j DROP
-A INPUT -s 10.1.1.3/32 -j DROP


Несколько однотипных правил, но сгруппировать их или сделать дополнительную цепочку не представляется возможным. В таких случаях, когда необходимо делать проверку по большому количеству IP-адресов и/или портов, на помощь приходит ipset. ipset представляет из себя модуль ядра ip_set, ряд вспомогательных библиотек и утилиту ipset для задания параметров. Наверняка есть в репозиториях вашего дистрибутива, так что с установкой не должно быть проблем.
Используется следующим образом:
* Создается список:

ipset -N dropips iphash
dropips — название списка, iphash — тип списка. Типы списков можно посмотреть в man ipset, они есть на любой вкус — для работы с ip-адресами, подсетями, портами, mac-адресами. iphash служит для хранения IP-адресов, использование хэширования предотвращает добавление в список дублирующих IP-адресов.
* Добавляется IP-адрес в список:

ipset -A dropips 10.1.1.1

* Создается правило для использования списка:

iptables -A INPUT -m set --set dropips src -j DROP
-m set указывает на использование модуля ipset, --set dropips указывает список IP-адресов, src указывает на то, что сверять нужно только IP-источника. Таким образом, будут отбрасываться все пакеты с IP-адресов, указанных в списке dropips.

В статье я не стал описывать тюнинг сетевых параметров ядра, а сделал упор на рациональную организацию правил файрволла. Информации о тюнинге очень много можно найти в сети, это различные net.*mem*, в случае большого количества соединений и использования conntrack — параметры net.netfilter.nf_conntrack_max, net.netfilter.nf_conntrack*timeout и т.д.

Спасибо за внимание.
Спасибо ivlis за указание на опечатки.
@peter23
карма
174,2
рейтинг 0,0
Пользователь
Самое читаемое Администрирование

Комментарии (17)

  • +3
    Шикарно, топик на главной, 12 добавили в избранное и ни одного коммента!
    Это говорит о качестве статьи, ни добавить, ни убавить. Профессионально.
  • 0
    Круто! Спасибо! Очень понравилась статья.
  • +3
    почему ipset не в ванили до сих пор?
  • –7
    ах, да — статья ни о чём.
  • +1
    Группировка правил — это, конечно, хорошо. Но в первую очередь надо не забывать первыми правилами в списке делать:
    -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
    -A INPUT -i lo -j ACCEPT
    • +1
      Ну это скорее относится не к оптимизации, а к базовой настройке файрволла.
  • 0
    Ув. автор, благодарствую, а вот не напишете ли статью о tc и шейпинге?.. (opennet.ru и google читал, но полного понимания сути работы пока не пришло...)
  • 0
    Собственно «module-assistant a-i ipset» — работает. Это радует.
  • –1
    а есть картинка из поста в большем расширении?
  • 0
    при большой нагрузке эффективнее использовать freebsd с ipfw и pf для ната(если надо)
    — мои 20 центов
    • 0
      Раскройте великую тайну, как при использовании natd с ipfw для ната добиться высокой производительности, если оно каждый пакет дважды в юзерспейс и обратно в ядро копирует?
      • 0
        я про natd ничего не писал. NAT выполняет pf
        • 0
          упс, сорри за невнимательность :)
  • 0
    В системе с множеством интерфейсов, его указание очень полезно.

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