2. Человеко-машинные системы
• Процедурные • Проективные
Набор функций (процедур) На языке инструментальной
внутри прикладной области составляется
области, описываемых в проект, описывающий ее
терминах прикладной предполагаемое поведение,
области. Набор решений затем машина его
ограничен. выполняет.
3. Процедурные системы
• Набор слабосвязанных предписаний
• Ограниченная осведомленность
• Гарантированные (простые) навыки
• Быстрота решения предусмотренной ситуации
Примеры:
– Пульт управления телевизором
– Мастер настройки Internet в Windows
4. Проективные системы
• Сложность решения задачи
• Информационная открытость: документация
• Ответственность
» Unix way:
ls -dt1 `grep -il отчет *` | head -1
5. История файрволов
• Сначала требовалась лишь статическая фильтрация
• Затем потребовалась динамика: отслеживание
соединений и поддержка NAT
• Затем потребовалась возможность легкого
расширения функционала
• Наконец, теперь требуется управляемость всем
этим разросшимся хозяйством
6. Статическая фильтрация
Основа любого нынешнего файрвола. Наиболее популярный
подход:
• набор правил (ruleset)
• каждое правило — отдельная логическая единица,
состоящая из одного или нескольких условий и одного (или
нескольких вспомогательных) действий, выполняемых при
совпадении (match) условий
• правила имеют разный приоритет
7. Статические правила: реализация
Классический подход (ipfilter, ipfw1, pf) — фиксированная
структура, описывающая все возможные варианты
проверок, поле флагов описывает, какие именно проверки
применены в данном конкретном правиле.
Недостатки:
• сложность расширения
• большой размер одного правила в байтах в ядре: у pf это
более 600 байт (см. pfrulepl в vmstat -z), тогда как
типичный размер cache-line процессора — 64 байта.
8. А в это время...
80-е — Berkley Packet Filter (BPF), виртуальная машина в ядре ОС
# tcpdump -d -s 123 src host 1.2.3.4
(000) ldh [12]
(001) jeq #0x800 jt 2 jf 4
(002) ld [26]
(003) jeq #0x1020304 jt 8 jf 9
(004) jeq #0x806 jt 6 jf 5
(005) jeq #0x8035 jt 6 jf 9
(006) ld [28]
(007) jeq #0x1020304 jt 8 jf 9
(008) ret #123
(009) ret #0
9. Ассемблер BPF
• Аккумулятор A
• Индексный регистр X
16 слов памяти M[]
•
• Опкоды (opcode, код операции, т. е. инструкция)
одинаковой длины (8 байт)
• DLT: Data Link Type
– #define DLT_EN10MB 1 /* Ethernet 10Mb */
– #define DLT_RAW 12 /* raw IP */
10. BSD/OS ipfw, 90-е
Базируется на расширенном BPF, в который
компилируется из своего языка:
implicit permit;
output interface(pif0) {
srcaddr(212.22.64.128/26) {
established {
permit;
}
switch ipprotocol {
case tcp:
switch dstport {
case pop3/tcp:
permit;
11. Точки входа в сетевом стеке
BSD/OS ipfw FreeBSD ipfw/pfil Linux iptables
pre-input PFIL_IN PREROUTING
input INPUT
forward FORWARD
pre-output* OUTPUT
output* PFIL_OUT POSTROUTING
12. output и
pre-output в
BSD/OS ipfw:
• Всё наоборот с
локальными
пакетами
• В других системах
forward → output
13. Linux iptables (netfilter, 2001)
• struct ipt_* (entry, match, ip, target, …)
• Переменная длина структур, в постоянных полях
ссылки на функции конкретного модуля
• Введены средства облегчения управляемости
(цепочки) и отслеживания соединений (conntrack)
как неотъемлемая часть самого файрвола
14. FreeBSD/NetBSD: pfil(9)
К локальным сокетам этой машины
ip_output()
ip_input()
ether_demux() ether_output()
Драйвера сетевых интерфейсов
16. NetGraph
• Коммуникационный объектно—ориентированный
фреймворк внутри ядра
• Граф в математике: узлы (nodes) и рёбра (edges)
• Граф в ядре: каждый узел имеет крючки (hooks),
соединение хуков двух узлов образует ребро, вдоль
которого передаются пакеты
• ОО-стиль: узлы управляются сообщениями (msg)
17. Команды в ngctl
+ mkpeer echo localhook hook1
+ name .:localhook myecho
+ show myecho:
Name: myecho Type: echo ID: 00001645 Num hooks: 1
Local hook Peer name Peer type Peer ID Peer hook
---------- --------- --------- ------- ---------
hook1 ngctl9010 socket 00001644 localhook
localhook hook1
myecho
ngctl9010
[ng_socket] [ng_echo]
18. Типы узлов
• Граничные (edge nodes) — взаимодействие с
остальной частью ядра
– ng_ether, ng_socket, ng_ipfw, ng_iface, ...
• Внутренние — взаимодействие только с
соседними узлами
– ng_echo, ng_tee, ng_bpf, ng_tag, ng_pppoe, ...
19. ng_ether(4)
ether_demux()
upper
В норме хуки ноды как бы
замкнуты между собой
lower
Драйвер интерфейса
23. Пример графа
Граф из ядра легко
можно
визуализировать в
пакете graphviz,
скормив ему
текстовый вывод
команды ngctl dot
24. А вот такое
автоматически
создает mpd
(фрагмент для одного
интерфейса ng3 в
соединенном состоянии)
25. Правила ipfw2
• Расширяемость была достигнута введением
опкодов, смоделированных наподобие BPF.
• FreeBSD следует POLA (принцип наименьшего
удивления пользователя), поэтому интерфейс,
основанный на правилах, требовалось сохранить
Гибридная схема: связный список правил, каждое
есть набор инструкций-опкодов
26. Опкоды
• Опкод начинается с номера, длины в 4-байтных словах
(возможные аргументы) и флагов
• На каждое элементарное действие: O_IP_SRC,
O_IP_SRC_MASK, O_IP_SRC_ME, O_IP_DSTPORT,
O_RECV, O_VERREVPATH, O_LOG, O_ACCEPT,
O_DIVERT и др.
• Парсер располагает сначала опкоды проверки условий,
затем в конце — опкоды действия (action) правила; каждый
опкод выставляет match в 0 или 1, при 0 исполнение
естественным образом прекращается, не доходя до действия
27. Конъюнктивная нормальная форма (КНФ)
• Каждый опкод имеет флаги F_OR и F_OR
• По умолчанию правило исполняется как
конъюнкция (AND) опкодов — первый же false
прекращает работу данного правила
• В случае выставленного F_OR действует OR-блок
— false переходит к следующему опкоду, true же
пропускает опкоды до конца OR-блока
(оптимизация выполнения)
28. Пример правила в КНФ
ipfw add deny { src-ip 1.2.3.4 or src-ip
1.2.3.5 or dst-ip 5.6.7.8} { not dst-port
80 or not dst-port 443 }
• O_IP_SRC, F_OR, 1.2.3.4
• O_IP_SRC, F_OR, 1.2.3.5
• O_IP_DST, 5.6.7.8
• O_IP_DSTPORT, F_OR|F_NOT, 80
• O_IP_DSTPORT, F_NOT, 443
• O_DENY
29. Динамические правила
• Коммутативная относительно
источника/назначения хэш-функция
• O_PROBE_STATE, O_CHECK_STATE, O_LIMIT,
O_KEEP_STATE
• Указатель на родительское правило: исполнение
начинается с action offset и продолжается дальше
как и в статическом варианте
30. Трюки делать можно, но...
• pf: одно правило с кейвордом reply-to
• ipfw:
add 100 skipto 300 tag 1 in recv $ext_if1 keep-state
add 200 skipto 300 tag 2 in recv $ext_if2 keep-state
add 300 allow { recv $ext_if1 or recv $ext_if2 }
add 400 allow in recv $int_if
add 500 fwd $gw1 tagged 1
add 600 fwd $gw2 tagged 2
31. Но лучше, чем когда нельзя
Задача: отфильтровать внутри транзитного
PPTP GRE src-адреса нашей клиентской
подсети
• pf: невозможно в принципе
• ipfw + netgraph позволяют это сделать, хотя и
дорогой ценой — сложные скрипты и
выражения для tcpdump
32. Выкурим кучу мануалов и начнем...
#define GRESTART IPHDRLEN(0)
/* Check that is GREv1 with seq num and proto set per RFC 2637 */
#define VALID_PPTP_GRE ((ip[GRESTART:4] & 0xff7fffff) = 0x3001880b)
/* ACK is optional 4 bytes to previous 12 */
#define GRE_DATA_START (GRESTART + ((ip[GRESTART+1] & 0x80) >> 5) + 12)
/* Actual IP subnet/Mask to find in the src IP of inner IP datagram */
#define SUBNET 0x52754000 /* 82.117.64.0 */
#define MASK 0xffffff00 /* 255.255.255.0 */
#define INNER_SRC_EQ_SUBNET(ppp_hdr_len) (ip[(GRE_DATA_START+ppp_hdr_len+12):4] & MASK =
SUBNET)
/* Finally, expression: sort by most frequent pattern first */
proto gre and VALID_PPTP_GRE and (
(
(ip[GRE_DATA_START]=0x21) and INNER_SRC_EQ_SUBNET(1)
) or (
(ip[GRE_DATA_START:2]=0xff03) and (ip[GRE_DATA_START+2]=0x21) and
INNER_SRC_EQ_SUBNET(3)
) or (
(ip[GRE_DATA_START:4]=0xff030021) and INNER_SRC_EQ_SUBNET(4)
) or (
(ip[GRE_DATA_START:2]=0x0021) and INNER_SRC_EQ_SUBNET(2)
)
)
34. А теперь — в netgraph!
#!/bin/sh
PATTERN=`cpp -P tcpdump-gre-addr-cpp`
BPFPROG=`tcpdump -s 8192 -r dltraw.pcap -ddd
$PATTERN | awk -f /tmp/bpf.awk`
...
ngctl mkpeer ipfw: bpf 190 $INHOOK
ngctl name ipfw:190 $NODENAME
ngctl msg $NODEPATH setprogram
{ thisHook=quot;$INHOOKquot; ifMatch=quot;$MATCHHOOKquot;
ifNotMatch=quot;$NOTMATCHHOOKquot; $BPFPROG }
ipfw add 4492 netgraph 190 gre from 82.117.64.0/24
to any iplen 60-1500
35. Что будем улучшать?
• Единый набор правил для всего — неудобен при большом
количестве правил (хотя можно сгенерировать правила
машиной)
• Надо сохранить управляемость машиной, привычный
интерфейс (кто хочет учить netgraph?) и возможность
знающему сотворить что-то, не укладывающееся в шаблоны
Делаем несколько наборов правил, каждый — как узел
netgraph, и человеческий интерфейс (кто хочет — прицепит
узлы нестандартным образом сам)
36. Интерфейс?
ipfw chain mychain create [default-policy
{allow | deny}]
ipfw chain mychain add 200 ...
ipfw chain mychain delete 300
ipfw chain mychain destroy
ipfw bind mychain to interface em0 direction
out
ipfw pfil-order move ipfilter last
37. Влечет за собой другие изменения
• Меняем API/ABI — setsockopt() требует правки других
файлов ядра при значительных изменениях типа таблиц;
сообщения netgraph же легко расширяемы и естественным
образом адресуются конкретному набору правил (узлу)
• Переведем на это же dummynet и введем в юзерлэнде
модульную структуру парсера
• Динамические правила: серьезная переработка
(единственный номер родительского правила недостаточен),
нет интерфейса управлением соединением
38. Пока мы здесь, надо это перетрясти
хорошенько и в других местах
Модули: более удобный к ним и netgraph
интерфейс, кейворды ngcall/ngmatch, вызов
наборов правил из других наборов правил
• ipfw ng_bpf mygreaddr create
• ipfw ng_bpf mygreaddr config `cpp -P
tcpdump-gre-addr-cpp`
• ipfw add deny ngmatch mygreaddr
39. Похоже на BPF?
Возьмем еще больше от BPF!
• Введем регистры как аналог M[]: хранение
информации о пакете, о динамическом
соединении, tablearg будет аналогом
аккумулятора A
• Вычисления над регистрами и данными
пакета
40. И пристыкуем
• Динамические правила — собственный небольшой
массив опкодов, использование регистров,
вычислений
– Хотите каждые 10 секунд медленно уменьшать
пакетам соединения TTL, а потом обратно? Без
проблем!
• Дополнительные обработчики соединений
• Операции над таблицами
41. Что еще?
• Наборы портов: add deny src-ip table(1) src-
port portset(1) dst-port portset(tablearg) …
• API/ABI для изменения состояния
динамических правил из netgraph и не
только
– отслеживание соединений
– возможность failover в carp(4)
42. Более мелкие изменения
• Расширим tablearg до uint64_t, таблицы для хранения
MAC-адресов, Ipv6
• Действия call/return внутри одного набора правил
• Операции над счетчиками и их проверка
• Быстрое сохранение/загрузка правил в бинарном виде
• Разворачивание и сворачивание типичных конструкций
алиасами в парсере (reply-to?)
• Ваши предложения? :-)
43. Планы на будущее
• Можно уйти от ориентации на правила и
сделать плоскую структуру опкодов, с
оптимизатором в парсере
– Позволит сделать описание конфигов на другом
языке, по типу BSD/OS или Juniper
– Проблемы со счетчиками, логами
– Позволит сделать компиляцию в машинный код
аналогично BPF JIT в 7.x
44. Если пофантазировать...
• Сделать ipfw узлом netgraph когда-то было
фантастической идеей, из перечня его
авторов
• Можно пойти дальше, перевести на netgraph
и другие части сетевого стека, создать
расширяемый визуальный конфигуратор
сетевой системы в едином стиле
45. Идеи носятся в воздухе
Совсем недавно команда разработчиков Netfilter обнародовала
сообщение о выпуске первой альфы нового файрвола для
Linux, грядущего на замену iptables.
include quot;ipv4-filterquot;
chain filter output {
ct state established,related accept
tcp dport 22 accept
counter drop
}
46. Чем оно отличается?
• Таблицы реализованы как хэши и RB-деревья
прямо в наборе правил
– нет возможности адресовать таблицу как
отдельную сущность
• Требование декомпиляции ограничивает
возможности по оптимизации
• Отсутствие обратной совместимости
• Фиксированные точки входа — не netgraph