Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Реорганизация 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,549 views

Published on

Published in: Technology
  • Be the first to comment

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. Спасибо за внимание!

×