Реорганизация IPFW и
NETGRAPH: новый подход к
контролю сетевого стека
Вадим Гончаров
Томский политехнический университет
Человеко-машинные системы
• Процедурные              • Проективные
Набор функций (процедур)   На языке инструментальной
  ...
Процедурные системы
•   Набор слабосвязанных предписаний
•   Ограниченная осведомленность
•   Гарантированные (простые) на...
Проективные системы
• Сложность решения задачи
• Информационная открытость: документация
• Ответственность

» Unix way:
ls...
История файрволов
• Сначала требовалась лишь статическая фильтрация
• Затем потребовалась динамика: отслеживание
  соедине...
Статическая фильтрация
Основа любого нынешнего файрвола. Наиболее популярный
  подход:
• набор правил (ruleset)
• каждое п...
Статические правила: реализация
Классический подход (ipfilter, ipfw1, pf) — фиксированная
  структура, описывающая все воз...
А в это время...
80-е — Berkley Packet Filter (BPF), виртуальная машина в ядре ОС
# tcpdump -d -s 123 src host 1.2.3.4
(00...
Ассемблер BPF
• Аккумулятор A
• Индексный регистр X
  16 слов памяти M[]
•
• Опкоды (opcode, код операции, т. е. инструкци...
BSD/OS ipfw, 90-е
Базируется на расширенном BPF, в который
  компилируется из своего языка:
implicit permit;
output interf...
Точки входа в сетевом стеке
BSD/OS ipfw   FreeBSD ipfw/pfil   Linux iptables
 pre-input        PFIL_IN         PREROUTING
...
output и
  pre-output в
 BSD/OS ipfw:
• Всё наоборот с
  локальными
  пакетами
• В других системах
  forward → output
Linux iptables (netfilter, 2001)
• struct ipt_* (entry, match, ip, target, …)
• Переменная длина структур, в постоянных по...
FreeBSD/NetBSD: pfil(9)
      К локальным сокетам этой машины


                                ip_output()
  ip_input()

...
FreeBSD: pfil(9)
• ipfw:
   pfil_add_hook(ipfw_check_in, NULL, PFIL_IN, pfh_inet);
   pfil_add_hook(ipfw_check_out, NULL, ...
NetGraph
• Коммуникационный объектно—ориентированный
  фреймворк внутри ядра
• Граф в математике: узлы (nodes) и рёбра (ed...
Команды в ngctl
+ mkpeer echo localhook hook1
+ name .:localhook myecho
+ show myecho:
  Name: myecho         Type: echo  ...
Типы узлов
• Граничные (edge nodes) — взаимодействие с
  остальной частью ядра
  – ng_ether, ng_socket, ng_ipfw, ng_iface,...
ng_ether(4)
                ether_demux()

upper

          В норме хуки ноды как бы
          замкнуты между собой

lower...
Обычный сокет
           int fd = socket(AF_INET);
Userland

Ядро
             Generic socket layer



             Машине...
ng_socket
           int fd = socket(AF_NETGRAPH);
Userland

Ядро
                Generic socket layer



                ...
ng_ksocket
Userland

Ядро


       Generic socket layer
                              Узел типа ng_ksocket


       Машине...
Пример графа
Граф из ядра легко
  можно
  визуализировать в
  пакете graphviz,
  скормив ему
  текстовый вывод
  команды n...
А вот такое
 автоматически
  создает mpd
(фрагмент для одного
интерфейса ng3 в
соединенном состоянии)
Правила ipfw2
• Расширяемость была достигнута введением
  опкодов, смоделированных наподобие BPF.
• FreeBSD следует POLA (...
Опкоды
• Опкод начинается с номера, длины в 4-байтных словах
  (возможные аргументы) и флагов
• На каждое элементарное дей...
Конъюнктивная нормальная форма (КНФ)
• Каждый опкод имеет флаги F_OR и F_OR
• По умолчанию правило исполняется как
  конъю...
Пример правила в КНФ
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-...
Динамические правила
• Коммутативная относительно
  источника/назначения хэш-функция
• O_PROBE_STATE, O_CHECK_STATE, O_LIM...
Трюки делать можно, но...
• pf: одно правило с кейвордом reply-to
• ipfw:
  add   100   skipto 300 tag 1 in recv $ext_if1 ...
Но лучше, чем когда нельзя
Задача: отфильтровать внутри транзитного
  PPTP GRE src-адреса нашей клиентской
  подсети
• pf:...
Выкурим кучу мануалов и начнем...
#define GRESTART IPHDRLEN(0)
/* Check that is GREv1 with seq num and proto set per RFC 2...
Совершенно нечитаемо, правда?
$ cpp -P tcpdump-gre-addr-cpp
proto gre and ((ip[((ip[0]&0xf)<<2):4] & 0xff7fffff) = 0x30018...
А теперь — в netgraph!
#!/bin/sh
PATTERN=`cpp -P tcpdump-gre-addr-cpp`
BPFPROG=`tcpdump -s 8192 -r dltraw.pcap -ddd
  $PAT...
Что будем улучшать?
• Единый набор правил для всего — неудобен при большом
  количестве правил (хотя можно сгенерировать п...
Интерфейс?
ipfw chain mychain create [default-policy
  {allow | deny}]
ipfw chain mychain add 200 ...
ipfw chain mychain d...
Влечет за собой другие изменения
• Меняем API/ABI — setsockopt() требует правки других
  файлов ядра при значительных изме...
Пока мы здесь, надо это перетрясти
   хорошенько и в других местах
Модули: более удобный к ним и netgraph
 интерфейс, кейв...
Похоже на BPF?
Возьмем еще больше от BPF!
• Введем регистры как аналог M[]: хранение
  информации о пакете, о динамическом...
И пристыкуем
• Динамические правила — собственный небольшой
  массив опкодов, использование регистров,
  вычислений
   – Х...
Что еще?
• Наборы портов: add deny src-ip table(1) src-
  port portset(1) dst-port portset(tablearg) …
• API/ABI для измен...
Более мелкие изменения
• Расширим tablearg до uint64_t, таблицы для хранения
  MAC-адресов, Ipv6
• Действия call/return вн...
Планы на будущее
• Можно уйти от ориентации на правила и
  сделать плоскую структуру опкодов, с
  оптимизатором в парсере
...
Если пофантазировать...
• Сделать ipfw узлом netgraph когда-то было
  фантастической идеей, из перечня его
  авторов
• Мож...
Идеи носятся в воздухе
Совсем недавно команда разработчиков Netfilter обнародовала
  сообщение о выпуске первой альфы ново...
Чем оно отличается?
• Таблицы реализованы как хэши и RB-деревья
  прямо в наборе правил
   – нет возможности адресовать та...
Спасибо за внимание!
Upcoming SlideShare
Loading in …5
×

Nuclight Ipfw Rootconf2009

1,374
-1

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,374
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
54
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Nuclight Ipfw Rootconf2009

  1. 1. Реорганизация IPFW и NETGRAPH: новый подход к контролю сетевого стека Вадим Гончаров Томский политехнический университет
  2. 2. Человеко-машинные системы • Процедурные • Проективные Набор функций (процедур) На языке инструментальной внутри прикладной области составляется области, описываемых в проект, описывающий ее терминах прикладной предполагаемое поведение, области. Набор решений затем машина его ограничен. выполняет.
  3. 3. Процедурные системы • Набор слабосвязанных предписаний • Ограниченная осведомленность • Гарантированные (простые) навыки • Быстрота решения предусмотренной ситуации Примеры: – Пульт управления телевизором – Мастер настройки Internet в Windows
  4. 4. Проективные системы • Сложность решения задачи • Информационная открытость: документация • Ответственность » Unix way: ls -dt1 `grep -il отчет *` | head -1
  5. 5. История файрволов • Сначала требовалась лишь статическая фильтрация • Затем потребовалась динамика: отслеживание соединений и поддержка NAT • Затем потребовалась возможность легкого расширения функционала • Наконец, теперь требуется управляемость всем этим разросшимся хозяйством
  6. 6. Статическая фильтрация Основа любого нынешнего файрвола. Наиболее популярный подход: • набор правил (ruleset) • каждое правило — отдельная логическая единица, состоящая из одного или нескольких условий и одного (или нескольких вспомогательных) действий, выполняемых при совпадении (match) условий • правила имеют разный приоритет
  7. 7. Статические правила: реализация Классический подход (ipfilter, ipfw1, pf) — фиксированная структура, описывающая все возможные варианты проверок, поле флагов описывает, какие именно проверки применены в данном конкретном правиле. Недостатки: • сложность расширения • большой размер одного правила в байтах в ядре: у pf это более 600 байт (см. pfrulepl в vmstat -z), тогда как типичный размер cache-line процессора — 64 байта.
  8. 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. 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. 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. 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. 12. output и pre-output в BSD/OS ipfw: • Всё наоборот с локальными пакетами • В других системах forward → output
  13. 13. Linux iptables (netfilter, 2001) • struct ipt_* (entry, match, ip, target, …) • Переменная длина структур, в постоянных полях ссылки на функции конкретного модуля • Введены средства облегчения управляемости (цепочки) и отслеживания соединений (conntrack) как неотъемлемая часть самого файрвола
  14. 14. FreeBSD/NetBSD: pfil(9) К локальным сокетам этой машины ip_output() ip_input() ether_demux() ether_output() Драйвера сетевых интерфейсов
  15. 15. FreeBSD: pfil(9) • ipfw: pfil_add_hook(ipfw_check_in, NULL, PFIL_IN, pfh_inet); pfil_add_hook(ipfw_check_out, NULL, PFIL_OUT, pfh_inet); • pf: pfil_add_hook(pf_check_in, NULL, PFIL_IN, pfh_inet); pfil_add_hook(pf_check_out, NULL, PFIL_OUT, pfh_inet); • ip_input(): pfil_run_hooks(&inet_pfil_hook, &m, m->m_pkthdr.rcvif, PFIL_IN, NULL); • pfil_list_add(): if (flags & PFIL_IN) TAILQ_INSERT_HEAD(list, pfh1, pfil_link); else TAILQ_INSERT_TAIL(list, pfh1, pfil_link);
  16. 16. NetGraph • Коммуникационный объектно—ориентированный фреймворк внутри ядра • Граф в математике: узлы (nodes) и рёбра (edges) • Граф в ядре: каждый узел имеет крючки (hooks), соединение хуков двух узлов образует ребро, вдоль которого передаются пакеты • ОО-стиль: узлы управляются сообщениями (msg)
  17. 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. 18. Типы узлов • Граничные (edge nodes) — взаимодействие с остальной частью ядра – ng_ether, ng_socket, ng_ipfw, ng_iface, ... • Внутренние — взаимодействие только с соседними узлами – ng_echo, ng_tee, ng_bpf, ng_tag, ng_pppoe, ...
  19. 19. ng_ether(4) ether_demux() upper В норме хуки ноды как бы замкнуты между собой lower Драйвер интерфейса
  20. 20. Обычный сокет int fd = socket(AF_INET); Userland Ядро Generic socket layer Машинерия TCP/IP
  21. 21. ng_socket int fd = socket(AF_NETGRAPH); Userland Ядро Generic socket layer Узел типа ng_socket
  22. 22. ng_ksocket Userland Ядро Generic socket layer Узел типа ng_ksocket Машинерия TCP/IP
  23. 23. Пример графа Граф из ядра легко можно визуализировать в пакете graphviz, скормив ему текстовый вывод команды ngctl dot
  24. 24. А вот такое автоматически создает mpd (фрагмент для одного интерфейса ng3 в соединенном состоянии)
  25. 25. Правила ipfw2 • Расширяемость была достигнута введением опкодов, смоделированных наподобие BPF. • FreeBSD следует POLA (принцип наименьшего удивления пользователя), поэтому интерфейс, основанный на правилах, требовалось сохранить  Гибридная схема: связный список правил, каждое есть набор инструкций-опкодов
  26. 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. 27. Конъюнктивная нормальная форма (КНФ) • Каждый опкод имеет флаги F_OR и F_OR • По умолчанию правило исполняется как конъюнкция (AND) опкодов — первый же false прекращает работу данного правила • В случае выставленного F_OR действует OR-блок — false переходит к следующему опкоду, true же пропускает опкоды до конца OR-блока (оптимизация выполнения)
  28. 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. 29. Динамические правила • Коммутативная относительно источника/назначения хэш-функция • O_PROBE_STATE, O_CHECK_STATE, O_LIMIT, O_KEEP_STATE • Указатель на родительское правило: исполнение начинается с action offset и продолжается дальше как и в статическом варианте
  30. 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. 31. Но лучше, чем когда нельзя Задача: отфильтровать внутри транзитного PPTP GRE src-адреса нашей клиентской подсети • pf: невозможно в принципе • ipfw + netgraph позволяют это сделать, хотя и дорогой ценой — сложные скрипты и выражения для tcpdump
  32. 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) ) )
  33. 33. Совершенно нечитаемо, правда? $ cpp -P tcpdump-gre-addr-cpp proto gre and ((ip[((ip[0]&0xf)<<2):4] & 0xff7fffff) = 0x3001880b) and ( ( (ip[(((ip[0]&0xf)<<2) + ((ip[((ip[0]&0xf)<<2)+1] & 0x80) >> 5) + 12)]=0x21) and (ip[((((ip[0]&0xf)<<2) + ((ip[((ip[0]&0xf)<<2)+1] & 0x80) >> 5) + 12)+1 +12):4] & 0xffffff00 = 0x52754000) ) or ( (ip[(((ip[0]&0xf)<<2) + ((ip[((ip[0]&0xf)<<2)+1] & 0x80) >> 5) + 12):2]=0xff03) and (ip[(((ip[0]&0xf)<<2) + ((ip[((ip[0]&0xf)<<2)+1] & 0x80) >> 5) + 12)+2]=0x21) and (ip[((((ip[0]&0xf)<<2) + ((ip[((ip[0]&0xf)<<2)+1] & 0x80) >> 5) + 12)+3 +12):4] & 0xffffff00 = 0x52754000) ) or ( (ip[(((ip[0]&0xf)<<2) + ((ip[((ip[0]&0xf)<<2)+1] & 0x80) >> 5) + 12):4]=0xff030021) and (ip[((((ip[0]&0xf)<<2) + ((ip[((ip[0]&0xf)<<2)+1] & 0x80) >> 5) + 12)+4 +12):4] & 0xffffff00 = 0x52754000) ) or ( (ip[(((ip[0]&0xf)<<2) + ((ip[((ip[0]&0xf)<<2)+1] & 0x80) >> 5) + 12):2]=0x0021) and (ip[((((ip[0]&0xf)<<2) + ((ip[((ip[0]&0xf)<<2)+1] & 0x80) >> 5) + 12)+2 +12):4] & 0xffffff00 = 0x52754000) ) )
  34. 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. 35. Что будем улучшать? • Единый набор правил для всего — неудобен при большом количестве правил (хотя можно сгенерировать правила машиной) • Надо сохранить управляемость машиной, привычный интерфейс (кто хочет учить netgraph?) и возможность знающему сотворить что-то, не укладывающееся в шаблоны  Делаем несколько наборов правил, каждый — как узел netgraph, и человеческий интерфейс (кто хочет — прицепит узлы нестандартным образом сам)
  36. 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. 37. Влечет за собой другие изменения • Меняем API/ABI — setsockopt() требует правки других файлов ядра при значительных изменениях типа таблиц; сообщения netgraph же легко расширяемы и естественным образом адресуются конкретному набору правил (узлу) • Переведем на это же dummynet и введем в юзерлэнде модульную структуру парсера • Динамические правила: серьезная переработка (единственный номер родительского правила недостаточен), нет интерфейса управлением соединением
  38. 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. 39. Похоже на BPF? Возьмем еще больше от BPF! • Введем регистры как аналог M[]: хранение информации о пакете, о динамическом соединении, tablearg будет аналогом аккумулятора A • Вычисления над регистрами и данными пакета
  40. 40. И пристыкуем • Динамические правила — собственный небольшой массив опкодов, использование регистров, вычислений – Хотите каждые 10 секунд медленно уменьшать пакетам соединения TTL, а потом обратно? Без проблем! • Дополнительные обработчики соединений • Операции над таблицами
  41. 41. Что еще? • Наборы портов: add deny src-ip table(1) src- port portset(1) dst-port portset(tablearg) … • API/ABI для изменения состояния динамических правил из netgraph и не только – отслеживание соединений – возможность failover в carp(4)
  42. 42. Более мелкие изменения • Расширим tablearg до uint64_t, таблицы для хранения MAC-адресов, Ipv6 • Действия call/return внутри одного набора правил • Операции над счетчиками и их проверка • Быстрое сохранение/загрузка правил в бинарном виде • Разворачивание и сворачивание типичных конструкций алиасами в парсере (reply-to?) • Ваши предложения? :-)
  43. 43. Планы на будущее • Можно уйти от ориентации на правила и сделать плоскую структуру опкодов, с оптимизатором в парсере – Позволит сделать описание конфигов на другом языке, по типу BSD/OS или Juniper – Проблемы со счетчиками, логами – Позволит сделать компиляцию в машинный код аналогично BPF JIT в 7.x
  44. 44. Если пофантазировать... • Сделать ipfw узлом netgraph когда-то было фантастической идеей, из перечня его авторов • Можно пойти дальше, перевести на netgraph и другие части сетевого стека, создать расширяемый визуальный конфигуратор сетевой системы в едином стиле
  45. 45. Идеи носятся в воздухе Совсем недавно команда разработчиков Netfilter обнародовала сообщение о выпуске первой альфы нового файрвола для Linux, грядущего на замену iptables. include quot;ipv4-filterquot; chain filter output { ct state established,related accept tcp dport 22 accept counter drop }
  46. 46. Чем оно отличается? • Таблицы реализованы как хэши и RB-деревья прямо в наборе правил – нет возможности адресовать таблицу как отдельную сущность • Требование декомпиляции ограничивает возможности по оптимизации • Отсутствие обратной совместимости • Фиксированные точки входа — не netgraph
  47. 47. Спасибо за внимание!

×