В различных версиях ядра Linux используются разные механизмы фильтрации сетевых пакетов.
Самый ранний с исторической точки зрения механизм ipfw, управляемый командой ipfwadm, использовался в ядрах 1.2 - 2.2.
В версии 2.2.10 появился механизим ipchains, который позволил создавать "подпрограммы" из правил, но ещё не умел отслеживать состояние соединений.
С версии 2.4 появился механизм с поддержкой состояний — netfilter, который управляется программой iptables.
С версии 3.13 добавлен механизм nftables, заменяющий iptables, ip6tables, arptables и ebtables. nftables управляется утилитой nft.
В настоящий момент основным механизмом предлагается сделать bpfilter, на основе виртуальной машины eBPF.
Надо различать механизмы сетевой фильтрации в ядре и прикладные программы, которые этими механизмами управляют.
iptables — явное создание, удаление, печать правил netfilter
UFW — генерация правил на основе существующих шаблонов в Ubuntu
ufw
— питоновский скрипт в Ubuntu для управление брандмауэром netfilter/iptables. Конфигурационные файлы находятся в каталоге /etc/ufw/
.
Перед включением брандмауэра удалённо по сети необходимо убедиться, что правила разрешают удалённый доступ по SSH. Для этого надо посмотреть правила в файле /etc/ufw/user.rules или выполнить добавление фиктивного правила с опцией --dry-run:
ufw --dry-run allow 1
У UFW есть пара недостатков:
Проверка состояния UFW:
ufw status verbose
Включение UFW
ufw enable
Отключение UFW
ufw disable
Установка правил по умолчанию
ufw default deny incoming
ufw default allow outgoing
Добавление разрешающих правил
ufw allow ssh
ufw allow 8080
ufw allow 80/tcp
Добавление запрещающих правил
ufw deny http
ufw deny from 123.45.67.89
Добавление правил в начало
ufw prepend allow http
Добавление правил в список по номеру
ufw status numbered
...
ufw insert 4 allow http
Добавление диапазонов портов
sudo ufw allow 7100:7150
Добавление разрешений по IP
ufw allow from 123.45.67.89
ufw allow from 123.45.67.0/24
ufw allow from 123.45.67.0/24 to any port 22
Удаление правил
ufw delete allow http
Удаление правил по номеру
ufw status numbered
...
ufw delete 2
Удаление всех правил
ufw reset
Если хочется изменить правила в цепочке FORWARD, то в начало правила надо добавить слово route
ufw route allow from 123.45.67.0/24
ufw route delete allow from 123.45.67.0/24
Включение/выключение сохранения логов в syslog (файлы /var/log/ufw*)
ufw logging <op>
Где <op>
Пакеты приложений могут добавлять списки необходимых им портов в файлы в каталоге /etc/ufw/applications.d. В последующем при задании правил вместо номера порта можно указывать app <NAME>
Список приложений:
ufw app list
Информация о конфигурации приложения
ufw app info <NAME>
Разрешить доступ к приложению. Имя приложения указывается вместо имени порта.
ufw allow <NAME>
ufw allow from 192.168.0.0/16 to any app <NAME>
firewalld позволяет описывать брандмауэр в терминах зон, сервисов и т.д. Конфигурация firewalld компилируется в правила iptables или nftables.
Список стандартных сервисов
firewall-cmd --get-services
Добавление разрешения на сервис или порт
firewall-cmd --permanent --add-service=ssh
firewall-cmd --permanent --add-port=4444/tcp
Удаление разрешения
firewall-cmd --permanent --remove-service=ssh
Список установленных разрешений
firewall-cmd --permanent --list-all
После всех установок необходимо их применить
firewall-cmd --reload
Подсистема netfilter/iptables в ядре отслеживает состояние соединений проходящих через NAT на маршрутизаторе.
Просмотреть текущие соединения можно командой
conntrack -L
Местоположение таблицы соединений в каталоге /proc может меняться в зависимости от версии ядра.
#new
cat /proc/net/nf_conntrack
#old
cat /proc/net/ip_conntrack
TCP Wrappers – изначально набор вспомогательных программ 'запускалок' (tcpd, miscd и вспомогательные программы), для контроля за IP адресами клиентов, стартующих сетевые сервисы через inetd. Основная функция, управляющая проверкой правил, hosts_access(request)
была в последующем выделена в отдельную библиотеку libwrap
. В настоящее время эту библиотеку используют самые разнообразные сервисы, такие как xinetd, sshd, rsyncs и т.п. Полный список программ, использующих TCP Wrappers, можно получить, выполнив следующую команду:
strings -f /usr/sbin/* /usr/local/sbin/* | grep hosts_access
Функция hosts_access
использует два конфигурационных файла /etc/hosts.allow
и /etc/hosts.deny
(в FreeBSD только /etc/hosts.allow
), которые могут иметь классический или расширенный синтаксис.
В классическом варианте вначале проверяются все правила из hosts.allow
. Если хоть одно правило применимо к паре сервис-клиент, то соединение устанавливается. Затем проверяются все правила из hosts.deny
. При нахождении соответствия соединение отклоняется. Если правила нет, то соединение устанавливается. Чтобы явно разрешить доступ только по правилам из hosts.allow
, необходимо добавить в hosts.deny
правило ALL:ALL
.
Классический синтаксис описан в man странице HOSTS_ACCESS(5). Файлы состоят из строк вида
daemon_list : client_list [ : shell_command ]
где
Файлы /etc/hosts.allow
и /etc/hosts.deny
эквивалентны по своему назначению. Каждое правило может содержать результат – allow или deny. Если в правиле результат не прописан явно, то он вычисляется по правилу классического варианта. Можно считать, что все правила находятся hosts.allow
, а hosts.deny
остается пустым. Выполняется первое найденное правило.
Синтаксис правил описан в man странице HOSTS_OPTIONS(5). Файлы состоят из строк вида
daemon_list : client_list : option [: option ...]
daemon_list, client_list полностью совместимы с классическим синтаксисом, а опции позволяют понизить привилегии, запустить дополнительную программу параллельно с сервисом или вместо него, и, наконец, принять решение allow или deny. Опции выполняются слева направо. Опция allow
/deny
должна стоять последней, поскольку после неё произойдёт запуск сервиса или прекращение работы.
Для запуска дополнительных программ используются две опции: spawn shell_command
– запуск программы в параллельном процессе одновременно с сетевым сервисом, и twist shell_command
– запуск программы в текущем процессе вместо сервиса. Командам в качестве параметров можно передать некоторую информацию о соединении. spawn не влияет на запуск основного сервиса, поэтому за ней должна следовать опция allow или deny. twist затирает текущий процесс и, соответственно, дальнейшие проверки опций. Однако, через параметры в twist можно передать имя исполняемого файла сервиса, чтобы выполнить его после дополнительных проверок.
Кроме опций запуска программ, существуют опции смены владельца, установки маски создаваемых файлов, понижение приоритета и т.п.
В конце списка правил можно добавить правило по умолчанию ALL:ALL:deny
, иначе подразумевается наличие ALL:ALL:allow
.
Список состоит из полей, разделённых пробелами и/или запятыми. Список может состоять из основной части и исключений, следующих за ключевым словом EXCEPT. Возможна группировка списков и исключений с помощью круглых скобок. В списке могут присутствовать IP адреса, имена хостов, ключевые слова и шаблоны.
Ключевые слова (часть опущена):
Шаблоны (часть опущена):
#классический стиль
sshd : .example.com
vsftpd : 192.168.
ALL : 192.168.1.0/255.255.255
# Разрешаем подключение из домена .example.com
# кроме station15.example.com
sshd : station15.example.com : deny
sshd : .example.com : allow
#Дополнительно запрещаем всем кроме домена *.hacker.org
sshd : ALL EXCEPT *.hacker.org:twist /bin/echo "Reject - bad host name %h"
# Фактически разрешаем *.hacker.org (остальные уже заблокированы)
# с логированием соединений в файл /tmp/sshd.connections
sshd : ALL:spawn /bin/echo "%d at %I connected from %i" > /tmp/sshd.connections: allow
# Запрещаем остальные соединения
ALL:ALL:deny
Для того, чтобы администратору не пришлось вникать в подробности работы с openssl
, вместе с OpenVPN поставляется набор скриптов для генерации ключей — EasyRSA.
Для работы OpenVPN с авторизацией по ключам требуется не менее семи файлов.
Серверная константа для Diffie-Hellman — dh1024.pem. Создается командой build-dh
.
Ключ и самоподписанный сертификат удостоверяющего центра (CA) — ca.key, ca.crt. Создаются командой build-ca
.
Ключ сервера и сертификат ключа сервера, подписанный ca.key, — server.key, server.crt. Создаются командой build-key-server
.
Ключи и сертификаты для клиентов. Сертификаты подписаны ca.key. Создаются командой build-key
или build-key-pass
.
В процессе работы скриптов создаются файлы запросов на сертификат с расширением .csr. Они не нужны и их можно спокойно стереть.
Конфигурация ключей на сервере:
dh /etc/openvpn/dh1024.pem
ca /etc/openvpn/ca.crt
cert /etc/openvpn/server.crt
key /etc/openvpn/server.key
Конфигурация ключей на клиенте:
ca ca.crt
cert client.crt
key client.key
Сертификат удостоверяющего центра ca.crt обычно один и тот же, что на сервере, что на клиенте. Он используется для верификации целостности собственного ключа (проверка, что ключ соответствует сертификату и что сертификат подписан CA) и для проверки сертификата партнера.
Если заменить (испортить) ca.crt то при попытке соединения выдается ошибка VERIFY ERROR: depth=0, error=unable to get local issuer certificate
С настройками по умолчанию EasyRSA генерирует сертификаты на десять лет. Через десять лет после выпуска сертификата CA срок его действия закончится на сервере и у всех клиентов. Кроме того, поскольку сертификат сервера обычно генерируют сразу вслед за созданием CA, в то же время закончится и срок действия сертификата сервера. При попытке подключиться клиентам выдается ошибка VERIFY ERROR: depth=0, error=certificate has expired: CN=server, serial=123
Для решения проблемы надо средствами openssl перевыпустить сертификат CA. Новый сертификат CA надо будет установить на сервере и у всех клиентов.
Генерация самоподписанного сертификата CA с использованием атрибутов из старого сертификата:
mv ca.crt old-ca.crt
openssl x509 -in old-ca.crt -days 3650 -out ca.crt -signkey ca.key
При завершении их срока действия сертификат сервера можно попробовать либо перевыпустить средствами openssl сертификат для имеющегося серверного ключа, либо сгенерировать новые ключ и сертификат средствами EasyRSA. При генерации нового ключа могут быть проблемы, поскольку удостоверяющий центр EasyRSA ведет учет выпущенных ключей и по умолчанию отказываться генерировать новый ключ, пока не отозван старый. Те же проблемы возникнут при завершения срока действия сертификатов клиентов. Чтобы остаться в рамках использования только EasyRSA можно поступить следующим способом:
После истечения срока сертификата CA можно создать новый CA. В этом случае необходимо перевыпустить новый сертификат для старого CA, как описано выше, после чего средствами EasyRSA создать новый CA и в дальнейшем использовать составной сертификат ca.crt, который будет содержать сертификаты старого и нового CA. В этом случае по мере устаревания сертификатов клиентов и серверов им просто выпускаются новые ключи в новом CA. Старые ключи в таком сценарии отзывать не надо, поскольку новый CA про них ничего не знает, а по истечению срока действия сертификата они автоматически станут недействительными.
Обновление сертификата средствами openssl:
openssl x509 -x509toreq -in server.crt -signkey ca.key -out server.csr
openssl x509 -in server.csr -out server.crt -signkey ca.key -req -days 3650
$ nmcli con add type vlan ifname VLAN10 dev enp1s0 id 10
Connection 'vlan-VLAN10' (37750b4a-8ef5-40e6-be9b-4fb21a4b6d17) successfully added.
Для запуска сетевой службы в стиле inetd, но через systemd необходимо создать два файла:
В файле сокета ListenStream - указывает на протокол TCP (для UDP используется параметр ListenDatagram=). В качестве значения ListenStream может быть указан номер порта или пара IP:Port. Параметр Accept=yes указывает на то, что для каждого подключения надо стартовать отдельный экземпляр программы. Чтобы проинформировать приложение о свойствах соединения, ему передаются переменные окружения REMOTE_ADDR и REMOTE_PORT.
/etc/systemd/system/baz.socket:
[Unit]
Description=Baz Socket
[Socket]
ListenStream=127.0.0.1:9999
Accept=yes
[Install]
WantedBy=sockets.target
Имя файла сервиса должно заканчиваться на @, чтобы указать, что одновременно может быть запущено много экземпляров данной программы. В данной конфигурации стандартный ввод программы будет назначен на /dev/null, стандартный вывод будет перенаправлен в сокет, сообщения об ошибках будут сохранены в логах journald. Программа может быть написана на чем угодно. Переменным окружения VAR1 и VAR2 будут назначены значения "word1 word2" и 'word3" соответственно, текущий каталог будет изменен на /tmp, вместо %i будет подставлено имя экземпляра программы, которое также будет видно в списке запущенных сервисов между @ и .service.
/etc/systemd/system/baz@.service:
[Unit]
Description=Baz Service
Requires=baz.socket
[Service]
Type=simple
Environment="VAR1=word1 word2" VAR2=word3
ExecStart=/usr/local/baz %i
WorkingDirectory=/tmp
StandardInput=socket
StandardOutput=socket
StandardError=journal
TimeoutStopSec=5
[Install]
WantedBy=multi-user.target
/usr/local/bin/baz:
#!/bin/bash
pwd
echo ${VAR1}
echo ${VAR2}
echo $1
Добавляем в файл /etc/sysctl.conf (или куда-нибудь в /etc/sysctl.d/50-noipv6.conf) строки
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
Обновляем текущую конфигурацию ядра sysctl -p
.
Применяем настройки к сетевым интерфейсам systemctl restart network
Проверяем адреса на сетевых интерфейсах ip addr
В конфигурации исправляем директиву Listen 80
на
Listen 0.0.0.0:80
Добавляем в squid.conf
dns_v4_first on
В main.cf
inet_protocols = ipv4
Для того, чтобы сам BIND не использовал IPv6 при запуске надо указать опцию -4
.
В CentOS это делается строчкой в файле /etc/sysconfig/named
OPTIONS="-4"
Дополнительно можно добавить в named.conf опцию
listen-on-v6 port 53 { none; };
Для того, чтобы BIND не выдавал записи AAAA (адреса IPv6) клиентам, подключенным по IPv4 необходимо добавить в файл named.conf опцию
filter-aaaa-on-v4 yes;
Для доступа к сетевым картам в Linux используются так называемые интерфейсы. Интерфейсы это не файлы устройств и их нет в каталоге /dev. Интерфейсы создаются динамически и не всегда связаны с сетевыми картами. Например интерфейс ppp0 - это интерфейс VPNа, организованного по протоколу PPTP, а интерфейс lo это виртуальная сетевая карта с адресом localhost (127.0.0.1). В Linux имена интерфейсов традиционно состоят из мнемонического типа интерфейса и его порядкового номера. Карты ethernet доступны через интерфейсы eth0, eth1 и т.д. В системах, использующих systemd способ именования другой - интерфейсы имеют имена вида enp2s0 (en -Ethernet, p - PCI, 2 - номер на шине) Список всех интерфейсов можно посмотреть командой ifconfig -a
или ip link
.
При наличии нескольких сетевых карт возникает вопрос о порядке их нумерации. В CentOS 6 эта задача возложена на подсистему обнаружения и конфигурации устройств - udev. В системах с systemd правила именования встроены в udev, но могот быть переопределены, как написано ниже.
Конфигурационный файл переименования интерфейсов обновляется автоматически при первом обнаружении очередной сетевой карты и находится в файле /etc/udev/rules.d/70-persistent-net.rules. Файл состоит из строк такого содержания:
# PCI device 0x1af4:0x1000 (virtio-pci)
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="52:54:00:29:24:1e", ATTR{type}=="1", KERNEL=="eth*", NAME="eth0"
Видно, что нумерация опирается на аппаратный (MAC) адрес карты. Если в компьютере заменить карту, то интерфейс eth0 станет недоступным, а новая карта получит имя eth1. Если есть желание вручную назначить имена картам, то можно отредактировать этот файл, выставив желаемые соответствия между MAC адресами именами интерфейсов (возможно потребуется перезагрузка).
Классическая утилита конфигурации сетевых интерфейсов ifcfg
#просмотр списка активных интерфейсов
ifcfg
#просмотр конфигурации конкретного интерфейса
ifcfg eth0
#просмотр списка доступных интерфейсов
ifcfg -a
#настройка ip адреса и активация интерфейса
ifconfig eth0 192.168.0.1 netmask 255.255.255.0 up
#деактивация интерфейса
ifconfig eth0 down
Настройка таблицы маршрутизации route
#добавление шлюза по умолчанию
route add default gw 192.168.0.1
#добавление маршрута в локальную сеть
route add -net 192.56.76.0 netmask 255.255.255.0 dev eth0
#добавление маршрута в удаленную сеть
route add -net 192.57.66.0 netmask 255.255.255.0 gw 192.168.0.2
#удаление маршрута
route del -net 192.57.66.0 netmask 255.255.255.0 gw 192.168.0.2
#просмотр таблицы маршрутизации
route
#или
netstat -r
В современных дистрибутивах линукса на смену ifconfig
и route
приходит универсальная утилита ip
#просмотр списка доступных интерфейсов
ip link
#просмотр статистики по интерфейсам
ip -s link
#просмотр ip адресов
ip addr
#просмотр таблицы маршрутизации
ip route
Адреса серверов DNS и имя локального домена вписываются в файл /etc/resolv.conf
search example.com
nameserver 8.8.8.8
Для просмотра доступных сетевых интерфейсов используется команда ip link
ArchLinux использует для настройки сети systemd/netctl
. Для конфигурации используются файлы профилей, которые хранятся в /etc/netctl/
. В данном каталоге есть подкаталог examples/
из которого можно копировать файлы с образцами профилей в /etc/netctl/
. Например:
cp /etc/netctl/examples/ethernet-static /etc/netctl/enp1s0-work
Содержимое enp1s0-work
после редактирования
Interface=enp1s0
Connection=ethernet
IP=static
Address=('10.1.10.2/24')
Gateway='10.1.10.1'
DNS=('10.1.10.1')
Базовые команды netctl
#активация профиля
netctl start enp1s0-work
#включение автоматической активации с текущими настройками профиля при старте системы
netctl enable enp1s0-work
#обновление конфигурации автостарта после изменения настроек профиля
netctl reenable enp1s0-work
В Ubuntu 20 система скриптов ifup
/ifdown
заменена программой netplan
, со своими конфигурационными файлами на языке YAML — /etc/netplan/имяфайла.yaml
См. также примеры на readthedocs.io.
Здесь только секция ethernets, но могут быть секции для vlan, bonding и т.д. Массивы имеют две альтернативные формы записи — в квадратных скобках и построчно, где каждая строка начинается с "- ". link-local: [] — запрет IPV6
network:
version: 2
renderer: networkd
ethernets:
enp3s0f0:
link-local: []
addresses:
- 192.168.56.110/24
routes:
- to: 172.16.0.0/24
via: 192.168.56.100
gateway4: 192.168.56.1
nameservers:
addresses: [8.8.8.8, 8.8.4.4]
Пример с DHCP
network:
version: 2
renderer: networkd
ethernets:
enp3s0:
dhcp4: true
Несколько IP на одном интерфейсе
network:
version: 2
renderer: networkd
ethernets:
enp3s0:
addresses:
- 10.100.1.37/24
- 10.100.1.38/24:
label: "enp3s0:0"
- 10.100.1.39/24:
label: "enp3s0:some-label"
netplan generate
— генерация из файлов YAML конфигураций для бэкендов NetworkManager или systemd-networkd в каталогах /run/каталог_бэкенда/. Здесь же проходит валидация синтаксиса.
netplan apply
— применение конфигурации
Просмотр доступных интерфейсов - ifcfg -a
Файл конфигурации интерфейсов - /etc/network/interfaces
. auto
- говорит о том, что интерфейс надо конфигурировать при старте системы.
iface eth0 inet static
address 192.168.0.1
netmask 255.255.255.0
gateway 192.168.0.254
auto eth0
iface eth1 inet dhcp
auto eth1
После смены настроек в /etc/network/interfaces
, необходимо отключить и снова включить интерфейс.
ifdown eth0
ifup eth0
Файл /etc/sysconfig/network используется стартовыми скриптами и содержит ключевые параметры - нужна ли сеть, нужно ли конфигурировать IP v6, имя компьютера. Сюда можно вписать шлюз по умолчанию, но CentOS 6 ориентируется на динамическое подкючение к сетям через WiFi и вписывает его в конфигурацию подходящего интерфейса.
NETWORKING=yes
NETWORKING_IPV6=no
HOSTNAME=wwww.example.com
GATEWAY=10.10.10.1
Файлы в каталоге /etc/sysconfig/network-scripts с именами вида ifcfg-eth0
Интерфейс, получающий адрес по DHCP
# динамическая конфигурация интерфейса
ONBOOT="yes"
DEVICE="eth0"
BOOTPROTO="dhcp"
Ручная конфигурация
# статическая конфигурация интерфейса
ONBOOT="yes"
DEVICE="eth1"
BOOTPROTO="static"
IPADDR=145.14.137.221
PREFIX=24
# конфигурация шлюза и DNS
GATEWAY=192.168.1.1
DEFROUTE=yes
DNS1=192.168.1.14
# NM - NetworkManager - графическая оболочка + автоматизация настроек
NM_CONTROLLED="no"
Дополнительный IP адрес на интерфейсе eth1
ONBOOT=no
DEVICE=eth1:0
BOOTPROTO=static
IPADDR=172.16.12.6
NETMASK=255.255.0.0
# следующие два параметра можно не писать
# они вычисляются из IP и маски
BROADCAST=172.16.255.255
NETWORK=172.16.0.0
Инициализация VLAN на eth1
DEVICE=eth1.72
VLAN=yes
VLAN_TRUNK_IF=eth1
BOOTPROTO=static
IPADDR=10.10.0.1
NETMASK=255.255.255.192
ONBOOT=yes
Файл /etc/resolv.conf
# мой домен, для подстановки в короткие имена
search example.com
# сервер DNS
nameserver 8.8.8.8
Ручная настройка имен хостов файл /etc/hosts
127.0.0.1 localhost.localdomain localhost ANY
::1 localhost6.localdomain6 localhost6
194.87.0.50 www.ru
Файл /etc/sysconfig/system-config-firewall правила для настройки программой system-config-firewall
Файл /etc/sysconfig/iptables текущие правила, сохраненные на случай перезагрузки.
После ручного изменения правил их можно сохранить командой service iptables save
Для написания сетевой службы в Linux не обязательно уметь программировать сокеты. Сокет, с точки зрения прикладной программы, выглядит как обычный файловый дескриптор, из которого можно читать и в который можно писать любыми стандартными функциями ввода/вывода. Поэтому появляется возможность перенаправить стандартный ввод/вывод любой программы в заранее подготовленный сокет. Для такого перенаправления служит программа xinetd
.
Конфигурация xinetd
, как правило, разделяется на две части: значения по умолчанию – /etc/xinetd.conf
и настройки для отдельных служб, по одному файлу в каталоге /etc/xinetd.d/
на службу.
Чтобы запустить свою сетевую службу, необходимо создать ещё один файл в /etc/xinetd.d/
. Имя файла особого значения не имеет, но обычно выбирается по имени службы. Предположим, что служба называется mynetd
, запускается от имени пользователя user1
и выполняет команду sleep 10
. Служба должна ожидать TCP соединение на порту 8888
.
Конфигурационный файл /etc/xinetd.d/mynetd
будет выглядеть так:
service mynetd
{
disable = no
# Если не написать UNLISTED, то xinetd будет искать имя службы и номер порта
# в файле /etc/services
type = UNLISTED
# stream = TCP, datagram = UDP
socket_type = stream
port = 8888
# Можно считать, что для протокола TCP wait всегда равен 'no',
# а для протокола UDP – всегда 'yes'
wait = no
user = user1
# При считывании конфигурации файл службы должен существовать и заданный пользователь
# должен иметь право на его выполнение
server = /bin/sleep
server_args = 10
}
Параметр wait
интерпретируется следующим образом: если socket_type = datagram
и wait = yes
, то при появлении на порту данных стартует новый процесс со входом/выходом, перенаправленным в сокет. Пока этот процесс не завершится, xinetd
не контролирует наличие новых данных на порту, оставляя их запущенной программе. Если поставить wait = no
, то на каждую дейтаграмму будет запускаться новый процесс, разделяющий со своими предшественниками общий сокет. В такой ситуации невозможно предсказать, какой копии программы достанутся входные данные из сокета. В принципе, такой подход возможен, если обработка дейтаграммы идёт долго, а писать многопоточный сервис лень. Для TCP ситуация обратная, wait = yes
имеет смысл только для многопоточных программ, иначе первый процесс захватит сокет и не отдаст его, пока программа не завершится.
Ограничение трафика на маршрутизаторе делается в два этапа:
1. Классификация пакетов с помощью netfilter или фильтров iproute2
2. Создание нескольких очередей пакетов с помощью iproute2
Пакеты распределяются в зависимости от класса на несколько очередей в которых ждут своей очереди на отправку. Каждая очередь имеет ограничения на полосу пропускания, приоритет и еще некоторые параметры. При переполнения очереди пакеты соответствующего класса начинают отбрасываться.
Очереди организованы в древовидную иерархию. Каждая очередь может быть разбита на более мелкие. Корневые очереди, связанные непосредственно с «хэндлером» интерфейса имеют абсолютные ограничения по пропускной способности. Дочерние очереди могут «заимствовать» друг у друга неиспользуемую полосу пропускания.
Классификация пакетов с iptables
Первый этап выполняется с помощью программы /sbin/iptables.
Маркируем пакеты в зависимости от каких либо параметров, например по размеру, номеру порта, ip адресу. Для специфических случаев можно дописывать свои модули для netfilter. Например модуль IP2P http://www.ipp2p.org позволяет выбирать пакеты p2p сетей.
Приписываем класс пакета в соответствии с проставленными меткам
Вообще говоря, можно было бы классифицировать пакеты сразу. Например выделим входящий HTTP трафик в отдельный класс
iptables -t mangle -A POSTROUTING -o eth0 -s 0/0 --source-port 80 -j CLASSIFY --set-class 1:10
Маркировка позволяет проверять только несколько первых пакетов в соединении. Для того, чтобы не исследовать каждый пакет используется модуль CONNMARK который позволяет маркировать все пакеты принадлежащие соединению.
Пример классификации P2P трафика. Классы для различных интерфейсов определяются независимо, что позволило использовать класс 1:20 для входящего и для исходящего трафика.
iptables -t mangle -A PREROUTING -p tcp -j CONNMARK --restore-mark
iptables -t mangle -A PREROUTING -p tcp -m mark ! --mark 0 -j ACCEPT
iptables -t mangle -A PREROUTING -p tcp -m ipp2p --ipp2p -j MARK --set-mark 1
iptables -t mangle -A PREROUTING -p tcp -m mark --mark 1 -j CONNMARK --save-mark
iptables -t mangle -A POSTROUTING -o eth0 -m mark --mark 1 -j CLASSIFY --set-class 1:20
iptables -t mangle -A POSTROUTING -o eth1 -m mark --mark 1 -j CLASSIFY --set-class 1:20
Создание очередей пакетов
Этап второй выполняется с помощью /sbin/tc.
Создаем связанную с интерфейсом очередь исходящих пакетов, указываем класс по умолчанию. Создаем корневой класс, назначаем лимит. Создаем дочерние классы, указываем лимиты для них (Обязательно должен быть создан класс используемый по умолчанию).
Теперь каждый пакет попадает в свою очередь. Если очередь переполняется, пакеты отбрасываются не доходя до аппаратного уровня.
Пример: Общая пропускная способность канала 10 Мбит/с. Внешний интерфейс eth0, внутренний eth1. Ограничиваем исходящий трафик P2P сетей из предыдущего примера полосой 32 Кбит/с, входящий 256 Кбит/с.
/sbin/tc qdisc add dev eth0 root handle 1: htb default 10
/sbin/tc class add dev eth0 parent 1: classid 1:1 htb rate 10mbit
/sbin/tc class add dev eth0 parent 1:1 classid 1:10 htb rate 10mbit
/sbin/tc class add dev eth0 parent 1:1 classid 1:20 htb rate 32kbit
/sbin/tc qdisc add dev eth1 root handle 1: htb default 10
/sbin/tc class add dev eth1 parent 1: classid 1:1 htb rate 10mbit
/sbin/tc class add dev eth1 parent 1:1 classid 1:10 htb rate 10mbit
/sbin/tc class add dev eth1 parent 1:1 classid 1:20 htb rate 256kbit
Пример стартового скрипта /etc/init.d/shaper (написан не мною и не связан с примером iptables)
#!/bin/sh
# init script written by shane at knowplace dot org
# this script only creates the qdiscs and classes required for shaping, it
# does NOT create the necessary filters
INTERFACE='eth0'
rc_done=" done"
rc_failed=" failed"
return=$rc_done
TC='/sbin/tc'
tc_reset ()
{
# Reset everything to a known state (cleared)
$TC qdisc del dev $INTERFACE root 2> /dev/null > /dev/null
}
tc_status ()
{
echo "[qdisc - $INTERFACE]"
$TC -s qdisc show dev $INTERFACE
echo "------------------------"
echo
echo "[class - $INTERFACE]"
$TC -s class show dev $INTERFACE
}
tc_showfilter ()
{
echo "[filter - $INTERFACE]"
$TC -s filter show dev $INTERFACE
}
case "$1" in
start)
echo -n "Starting traffic shaping"
tc_reset
U320="$TC filter add dev $INTERFACE protocol ip parent 1:0 prio 0 u32"
#
# dev eth0 - creating qdiscs & classes
#
$TC qdisc add dev $INTERFACE root handle 1: htb default 60
$TC class add dev $INTERFACE parent 1: classid 1:1 htb rate 116kbit
$TC class add dev $INTERFACE parent 1:1 classid 1:10 htb rate 32kbit ceil 116kbit prio 0
$TC class add dev $INTERFACE parent 1:1 classid 1:20 htb rate 22kbit ceil 116kbit prio 1
$TC class add dev $INTERFACE parent 1:1 classid 1:30 htb rate 22kbit ceil 116kbit prio 2
$TC class add dev $INTERFACE parent 1:1 classid 1:40 htb rate 20kbit ceil 116kbit prio 3
$TC class add dev $INTERFACE parent 1:1 classid 1:50 htb rate 18kbit ceil 116kbit prio 4
$TC class add dev $INTERFACE parent 1:1 classid 1:60 htb rate 2kbit ceil 116kbit prio 5
$TC qdisc add dev $INTERFACE parent 1:10 handle 10: sfq perturb 10
$TC qdisc add dev $INTERFACE parent 1:20 handle 20: sfq perturb 10
$TC qdisc add dev $INTERFACE parent 1:30 handle 30: sfq perturb 10
$TC qdisc add dev $INTERFACE parent 1:40 handle 40: sfq perturb 10
$TC qdisc add dev $INTERFACE parent 1:50 handle 50: sfq perturb 10
$TC qdisc add dev $INTERFACE parent 1:60 handle 60: sfq perturb 10
tc_status
;;
stop)
echo -n "Stopping traffic shaper"
tc_reset || return=$rc_failed
echo -e "$return"
;;
restart|reload)
$0 stop && $0 start || return=$rc_failed
;;
stats|status)
tc_status
;;
filter)
tc_showfilter
;;
*)
echo "Usage: $0 {start|stop|restart|stats|filter}"
exit 1
esac
test "$return" = "$rc_done" || exit 1
##################################
Примечания.
В параметрах iptables цель CLASSIFY в iptables может использоваться только в таблице mangle и только в цепочке POSTROUTING.
В параметрах tc встречаются сокращения:
qdisc – Classful Queuing Disciplines – базовый объект связанный с сетевым интерфейсом и обеспечивающий управление очередями
htb – Hierarchical Token Bucket – имя собственное одного из планировщиков очередей пакетов в ядре Linux
Имена классов имеют вид M:N. M: эквивалентно M:0 и используется для создания общего корня очередей (хэндлера) связанного напрямую с интерфейсом.