Шейпер для больших сетей

Материал из ALT Linux Wiki
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)

Шейпер для больших сетей (IPv4/IPv6) средствами iptables, ipset, tc

Особенности работы с большими сетями

В случае, если необходимо назначать индивидуальную скорость для сотен и тысяч адресов в сети, встроенный в etcnet инструментарий eqos становится мало пригодным. По той причине, что для отработки одного правила, в shell-скриптах производится десятки вызовов внешних программ и других скриптов. В результате останов и запуск сайтов с настройками для 3 тыс. классов может растянуться до получаса. В eqos из etcnet фильтры tc создаются линейного типа. В этом случае пакет будет обходить все правила фильтрации, пока не попадет на подходящий, либо не будет отправлен в фильтр по-умолчанию. При количестве правил фильтрации в несколько тысяч, и трафике в десятки тысяч пакетов в секунду, растет загрузка CPU системы и параллельно растут задержки прохождения транзитных пакетов. Это, в свою очередь, отрицательно влияет на комфортность работы с сетью. Аналогичные последствия и при злоупотреблении линейными правилами фильтрации в iptables/ip6tables.

Поэтому не используем eqos, сводим до минимума количество правил в iptables/ip6tables и в tc отказываемся от линейных фильтров.

В случае сети, в которой используется только IPv4, фильтры tc можно построить на основе хэш-таблиц


Предварительные требования к системе

В системе должен быть установлены пакеты

  • iproute2-3.8.0-alt1 и новее;
  • ipset-6.23-alt1 и новее;
  • kernel-modules-ipset-*-6.24-alt1 и новее;
  • iptables-1.4.21-alt1.M70P.1 и новее;


Создание дисциплин, классов и очередей

Так как управление трафиком для сотен и тысяч адресов в ручном режиме никто в здравом уме делать не будет, все приведенные здесь конфигурационные файлы есть результат выполнения сторонних программ и скриптов, если это особо не указано. Все параметры для tc и ipset загружаются в batch-режиме.

Итак, в качестве корневой дисциплины была выбрана hfsc, как более экономная к процессорному времени, и ее реализация в ядре linux не имеет global-locks, в отличии от htb, в результате чего нагрузка эффективно распределяется по всем процессорным ядрам в системе.

Пример batch-файла для tc:

/tmp/shape-tc.hfsc:
# bondEXT - интерфейс в сторону мира
# bondINT - интерфейс в сторону клиентов
#
# Чистим корневую дисциплину от всех предыдущих настроек
qdisc del dev bondEXT root
qdisc del dev bondINT root

# назначаем корневую дисциплину, и определяем класс по-умолчанию для трафика, не попавшего ни в один определеный класс
qdisc add dev bondEXT root handle 1: est 1sec 8sec hfsc default ffff
qdisc add dev bondINT root handle 1: est 1sec 8sec hfsc default ffff

# создаем корневой класс и определяем его параметры
class add dev bondEXT parent 1: classid 1:1 est 1sec 8sec hfsc sc rate 2Gbit ul rate 2Gbit
class add dev bondINT parent 1: classid 1:1 est 1sec 8sec hfsc sc rate 2Gbit ul rate 2Gbit

# создаем клдасс по-умолчанию для трафика, не попавшего в определенный класс
# !!!! ВАЖНО!!!! этот класс, как и фильтр по-умолчанию должен быть создан.
filter add dev bondEXT parent 1: protocol ip prio 1000 u32 match u32 0 0 classid 1:ffff
filter add dev bondINT parent 1: protocol ip prio 1000 u32 match u32 0 0 classid 1:ffff
class add dev bondEXT parent 1: classid 1:ffff est 1sec 8sec hfsc sc umax 1500 dmax 150ms rate 1000kbit ul rate 100Mbit
class add dev bondINT parent 1: classid 1:ffff est 1sec 8sec hfsc sc umax 1500 dmax 150ms rate 1000kbit ul rate 100Mbit
qdisc add dev bondEXT parent 1:ffff handle ffff: pfifo limit 50000
qdisc add dev bondINT parent 1:ffff handle ffff: pfifo limit 50000

Пример правил для непосредственно управляемых адресов:

class add dev bondINT parent 1: classid 1:b est 1sec 8sec hfsc sc umax 1500b dmax 10ms rate 92160kbit ul rate 92160kbit
qdisc add dev bondINT parent 1:b handle b: pfifo limit 200
class add dev bondEXT parent 1: classid 1:b est 1sec 8sec hfsc sc umax 1500b dmax 10ms rate 92160kbit ul rate 92160kbit
qdisc add dev bondEXT parent 1:b handle b: pfifo limit 200
# 
class add dev bondINT parent 1: classid 1:c est 1sec 8sec hfsc sc umax 1500b dmax 5ms rate 2048kbit ul rate 2048kbit
qdisc add dev bondINT parent 1:c handle c: pfifo limit 200
class add dev bondEXT parent 1: classid 1:c est 1sec 8sec hfsc sc umax 1500b dmax 5ms rate 2048kbit ul rate 2048kbit
qdisc add dev bondEXT parent 1:c handle c: pfifo limit 200
# 
class add dev bondINT parent 1: classid 1:d est 1sec 8sec hfsc sc umax 1500b dmax 5ms rate 20480kbit ul rate 20480kbit
qdisc add dev bondINT parent 1:d handle d: pfifo limit 200
class add dev bondEXT parent 1: classid 1:d est 1sec 8sec hfsc sc umax 1500b dmax 5ms rate 20480kbit ul rate 20480kbit
qdisc add dev bondEXT parent 1:d handle d: pfifo limit 200

...

class add dev bondINT parent 1: classid 1:d44 est 1sec 8sec hfsc sc umax 1500b dmax 10ms rate 30720kbit ul rate 30720kbit
qdisc add dev bondINT parent 1:d44 handle d44: pfifo limit 200
class add dev bondEXT parent 1: classid 1:d44 est 1sec 8sec hfsc sc umax 1500b dmax 10ms rate 30720kbit ul rate 30720kbit
qdisc add dev bondEXT parent 1:d44 handle d44: pfifo limit 200

Классом 1:b назначена скорость 90Mbit, 1:c - 2Mbit, 1:d - 20Mbit и так далее вплоть до 1:d44 - 30Mbit. Обратите внимание, что в tc определения для классов, очередей и фильтров задаются в шестнадцатиричной системе в диапазоне 1-ffff.


Классификация трафика

Классифицировать трафик можно несколькими способами. Методы u32 match ip dst|src и handle MARK fw flowid нам не подходят, особенно второй метод, когда пакет анализируется два раза - один раз в iptables, когда выставляется MARK, и второй раз, когда помаркорованный пакет вторично анализируется в фильтре tc. Более производителей метод с использованием хэш-таблиц, более подробно можно почитать тут - [1]. Но этот метод подходит только когда в сети используется исключительно IPv4. Отдельно для IPv6 хэштаблицы не реализованы, а обходные методы не обеспечивают должной гибкости и простоты понимания для человека.

Поэтому был использован третий способ.

В ipset, начиная с версии 6.22 была добавлена очень полезный инструмент - оперированием структурой skb на основании правил iptables/ip6tables. Теперь логику классификации можно вынести в правила iptables и ipset.

Для удобства, здесь используется функционал etcnet.

  • Создаем сеты, в которые будем вносить адреса:
IPv4
/etc/net/ifaces/default/fw/ipset/nethash/shaper4:
    family inet skbinfo
IPv6
/etc/net/ifaces/default/fw/ipset/nethash/shaper6:
    family inet6 skbinfo
  • Создаем правила iptables, которые будут производить классификацию трафика:
IPv4
/etc/net/ifaces/default/fw/iptables/mangle/POSTROUTING:
    -o bondEXT -j SET --map-set shaper4 src --map-prio
    -o bondINT -j SET --map-set shaper4 dst --map-prio
IPv6
/etc/net/ifaces/default/fw/ip6tables/mangle/POSTROUTING:
    -o bondEXT -j SET --map-set shaper6 src --map-prio
    -o bondINT -j SET --map-set shaper6 dst --map-prio
  • С помощью стороннего скрипта генерируем batch-файл для ipset такого вида:
/tmp/shape-set.txt:
flush shaper4
flush shaper6
#
add shaper4 172.16.17.196/32 skbprio 1:b
#
add shaper4 172.16.17.220/32 skbprio 1:c
#
add shaper4 172.16.1.2/32 skbprio 1:d
add shaper4 172.16.1.5/32 skbprio 1:d
add shaper4 172.16.1.10/32 skbprio 1:d
add shaper4 172.16.1.12/32 skbprio 1:d
add shaper4 172.16.1.14/32 skbprio 1:d
add shaper4 172.16.1.31/32 skbprio 1:d
add shaper4 172.16.1.103/32 skbprio 1:d
add shaper4 172.16.1.128/32 skbprio 1:d
add shaper4 172.16.1.184/32 skbprio 1:d
add shaper4 172.16.2.115/32 skbprio 1:d
add shaper4 172.16.5.198/32 skbprio 1:d
add shaper4 172.16.6.38/32 skbprio 1:d
add shaper4 172.16.6.58/32 skbprio 1:d
add shaper4 172.16.6.69/32 skbprio 1:d
add shaper4 172.16.9.228/32 skbprio 1:d
add shaper4 172.16.10.208/32 skbprio 1:d
add shaper4 172.16.11.67/32 skbprio 1:d
add shaper4 172.16.11.68/32 skbprio 1:d
add shaper4 172.16.11.147/32 skbprio 1:d
add shaper4 172.16.15.213/32 skbprio 1:d
add shaper4 172.16.15.214/32 skbprio 1:d
add shaper4 172.16.17.75/32 skbprio 1:d

...

add shaper4 172.16.17.231/32 skbprio 1:d44
add shaper6 XXXX:4680:26:0:0:0:0:202/124 skbprio 1:d44

обратите внимание, что адреса IPv4 заносятся в сет shaper4, адреса для IPv6 - в shaper6. В этих сетах и происходит присвоение трафику определенные классы. Соответствие идентификаторов классов в batch-файлах tc и ipset возлагается на внешний скрипт генерации.

Загрузка параметров в систему

Загрузка правил производится по крону, запуском такого скрипта:

/usr/local/sbin/shaper.sh:
#!/bin/sh

ВЫЗОВ_СКРИПТА_ГЕНЕРИРУЮЩЕГО_shape-tc.hfsc_И_shape-set.txt

if [ ! -f /tmp/shape-tc.old ]; then
    touch /tmp/shape-tc.old
fi

if [ ! -f /tmp/shape-set.old ]; then
    touch /tmp/shape-set.old
fi

# данные меняются не так часто, поэтому реальная я перезагрузка
# производится только когда что-то поменялось
T=`diff /tmp/shape-tc.old /tmp/shape-tc.hfsc`
if [ "$T" != "" ]
then
    /sbin/tc -force -batch /tmp/shape-tc.hfsc > /dev/null 2>&1
    /sbin/ipset -! restore < /tmp/shape-set.txt > /dev/null 2>&1
    mv -f /tmp/shape-tc.hfsc /tmp/shape-tc.old
    mv -f /tmp/shape-set.txt /tmp/shape-set.old
else
    T=`diff /tmp/shape-set.old /tmp/shape-set.txt`
    if [ "$T" != "" ]
    then
        /sbin/tc -force -batch /tmp/shape-tc.hfsc > /dev/null 2>&1
        /sbin/ipset -! restore < /tmp/shape-set.txt > /dev/null 2>&1
        mv -f /tmp/shape-tc.hfsc /tmp/shape-tc.old
        mv -f /tmp/shape-set.txt /tmp/shape-set.old
    fi
fi

Полисинг для больших сетей (IPv4/IPv6) средствами nftables

Отличия полсинга от шейпера

Важным отличием полисера от шейпера в том, что полисер при своей работе безусловно отбрасывает те передаваемые пакеты, которые создают превышение по заданной полосе пропускания, в то время как шейпер за счет применения алгоритмов буферизации сглаживает пики превышения. Шейпер оптимально работает в случаях, когда требуется достаточно точно задавать полосы пропускания до десятков мегабит в секунду, при более высоких скоростях происходит большой перерасход памяти на буферизацию задершиваемых данных, высокой нагрузке CPU и лавинообразному падению суммарной производительности системы. Полисер лишен этих недостатков, с его помощью можно задавать полосы пропускания от десятков мегабит до десятков гигабит в секунду.

Реализация средствами nftables

Используя подсистему nftables все правила классификации трафика и присвоения полосы реализуются одним инструментов в одном месте. И за счет того, что правила фильтрации предварительно компилируются в пространстве пользователя и потом загружаются и исполняются в BPF виртуальной машине, значительно повышается производительность системы.

Предварительные требования к системе

В системе должен быть установлены пакеты

  • kernel-image-std-def-5.15.23-alt1 и новее
  • libmnl-1.0.4-alt2
  • libnftnl-1.2.1-alt1
  • nftables-1.0.1-alt1

Предварительная конфигурация

Здесь мы создаем таблицу mangle с типом inet, при котором обрабатывается и ipv4 и ipv6 трафик, задаем какие подсети у нас являются локальными, создаем verdict-сеты для ipv4 и ipv6 адресов клиентов на входящий и исходящий трафик, задаем полосу пропускания для локального трафика (обратите внимание, скорость задается в байтах, килобайтах, мегабайтах в секунду) и в цепочках POSTROUTING и PREROUTING указываем правила классификации трафика через механизм vmap

Создаем файл mangle.nft:

table inet mangle {

set localnet4 {
        type ipv4_addr
        flags interval
        elements = {
            5.xxx.xxx.0/22,
            193.xxx.xxx.0/23,
            100.64.0.0/10,
            172.16.0.0/12,
            10.0.0.0/16,
            10.1.1.0/24
        }
}

set localnet6 {
        type ipv6_addr
        flags interval
        elements = {
            fe80::/10,
            2a0e:xxxx::/29,
            fd00::/8
        }
}

    map poly_u_4 {
        type ipv4_addr : verdict
        flags interval
        counter
    }

    map poly_d_4 {
        type ipv4_addr : verdict
        flags interval
        counter
    }

    map poly_u_6 {
        type ipv6_addr : verdict
        flags interval
        counter
    }

    map poly_d_6 {
        type ipv6_addr : verdict
        flags interval
        counter
    }

    chain POSTROUTING {
        type filter hook postrouting priority mangle; policy accept;
        ip daddr  @localnet4 ip  saddr @localnet4 goto inet_down
        ip6 daddr @localnet6 ip6 saddr @localnet6 goto inet_down
        ip daddr vmap @poly_d_4
        ip6 daddr vmap @poly_d_6
    }

    chain PREROUTING {
        type filter hook prerouting priority mangle; policy accept;
        ip daddr  @localnet4 ip  saddr @localnet4 goto inet_down
        ip6 daddr @localnet6 ip6 saddr @localnet6 goto inet_down
        ip saddr vmap @poly_u_4
        ip6 saddr vmap @poly_u_6
    }

    chain inet_down {
        # If from localnet - accept
        limit rate over 10000000 kbytes/second counter drop
    }
}

Загружаем его через nft -o -f mangle.nft

Правила для задания полосы пропускания

Применение nft add chain / nft add element на массивах в тысячи едениц крайне непроищводительно, то по данным из биллинга скриптами генерим файл конфигурации такого вида:

table inet mangle {

chain policer_dl_10372 {
    limit rate over 640 kbytes/second burst 128 kbytes counter drop
}
chain policer_ul_10372 {
    limit rate over 640 kbytes/second burst 128 kbytes counter drop
}
chain policer_dl_10612 {
    limit rate over 192 kbytes/second burst 38 kbytes counter drop
}
chain policer_ul_10612 {
    limit rate over 192 kbytes/second burst 38 kbytes counter drop
}
map poly_d_4 {
    type ipv4_addr : verdict
    flags interval
    elements = {
100.64.2.102/32 : goto policer_dl_10372,
100.64.2.73/32 : goto policer_dl_10612 }
}
map poly_u_4 {
    type ipv4_addr : verdict
    flags interval
    elements = {
100.64.2.102/32 : goto policer_ul_10372,
100.64.2.73/32 : goto policer_ul_10612 }
}
  map poly_d_6 {
    type ipv6_addr : verdict
    flags interval
    elements = {
2a0e:xxxx:xxxx:e83:0:0:0:0/64 : goto policer_dl_10612 }
}
  map poly_u_6 {
    type ipv6_addr : verdict
    flags interval
    elements = {
2a0e:xxxx:xxxx:e83:0:0:0:0/64 : goto policer_ul_10612 }
}
}

И загружаем его в один прием через

nft -o -f filename.nft

В результате новые цепочки и элементы vmap будут добавлены к существующему содержимому таблицы mangle.

Итоговый результат можно посмотреть через:

nft list table inet mangle