3. Вместо содержания
• Заглянем немного в историю
• Оценим нынешнее состояние сетевого стека
• Перейдём от теории к практике
• Рассмотрим OpenBSD made stuff (pf, bgpd, ospfd, carp, etc)
4. Ты помнишь, как всё начиналось ?
Создание MULTICS (ARPA)
• первый сетевой стек (MIT, 1970)
• стек MULTICS – основа большинства иных стеков
Первый TCP/IP стек (MULTICS, 1980)
Первая UNIX реализация (Berkeley, 1983)
Основа стека OpenBSD – стек 4.4BSD
• TCP/IP Illustrated, vol. 2 всё ещё руководство № 1
5.
6. Краткий обзор
Socket API
• взаимодействие с userland
mbuf “API”
• управление памятью
Сетевой стек
• реализации протоколов
Маршрутизация
• независимая реализация на уровне ядра
7. Socket API
Используется userland-ом для осуществления
сетевого взаимодействия
• IPC де факто стандарт
• socket(), bind(), connect(), accept()
• recv(), send(), …
• setsockopt(), getsockopt()
Тема отдельного разговора (выходит далеко за
рамки данного доклада)
• косвенное упоминание для полноты «картины»
9. mbufs
Аллокация памяти для пакетов (256 байт каждый)
• иногда используются для иных данных
Могут образовывать цепочки и расширяться за счёт
внешних буферов (кластер - 2K)
Формируются посредством системного pool(9)
Неформальный API == набор функций
• 1-я причина сетевых багов – неправильная работа с mbuf!
10.
11. mbuf tags
Ядро может имплементировать метаданные в пакет
• чаще всего для предотвращения петлей
(IPsec, gif(4), gre(4))
Структура
• TLV – type, length, value
• динамический размер, выделяемый malloc(9)
• элементы формируют линейный список
13. mbuf tags – хак pf(4)
pf(4) – активный пользователь mbuf tags
Каждый пакет получал tag до того, пока …
Метаданные pf не были имплементированы
непосредственно в заголовок пакета
• заголовок немного увеличился
• отсутствие «холостого хода» malloc(9)
• оставшегося места достаточно для малых пакетов
Пример – x2 прирост сквозь SOEKRIS коробку
18. Уровень интерфейса (input)
Обработчик RX прерывания драйвера «достаёт» пакет из
DMA кольца
Данные попадают на канальный уровень
• пример - ether_input()
• определение сетевого протокола
Пакет передаётся во входящую протокольную очередь с
последующим планированием программного прерывания
• пример - schednetisr(NETISR_IP); inq = &ipintrq;
• netisr – splnet() vs. splsoftnet() (зона демаркации прерываний)
• оставшиеся части стека выполняются на более низком уровне
20. Сетевой уровень (input)
Netisr вызывает сетевой обработчик
• пример – ipintr() == программное прерывание
• при условии наличия данных
Входящие pf(4) фильтрации
Назначение пакета не локально?
• перенаправить (if enabled), иначе drop
• восстановление пакета (TTL, checksum)
• передать на обработку ip_output()
Переход на следующий уровень
• посредством protosw / inetsw [] структуры
• каждый протокол уровня содержит структуру protosw
22. Транспортный уровень (input)
Определение блока контроля протокола (PCB)
• каждое открытое соединение имеет PCB
• вмещает всю необходимую информацию для установления
соединения
Надёжным протоколам – сложный код
(TCP – целая философия)
• управление потоком
• повторная передача
• логика повторной сборки
• подтверждения
• SYN кеш
Передача в очередь буфера сокета и активизация userland
25. Транспортный уровень (output)
Userland: write(), send(), …
Уровень сокета использует структуру protows для передачи
данных
• каждая операция сокета в контексте – вызов функции pr_usrreq(), где
pr – конкретный протокол (например, tcp_usrreq())
pr_usrreq() вызывает output функцию
управление потоком, повторная передача, … для TCP
вызов сетевой output функции напрямую (без protosw)
• специфика работы UDP (псевдо IP == PIP)
• причина необходимости использования udp_output() и udp6_output()
27. Сетевой уровень (output)
Формирование сетевого (IP, IPv6) заголовка
Поиск маршрута (route lookup)
• В большинстве случаев – маршруты из кеша
Исходящие pf(4) фильтрации
Вызов output функции интерфейса
• интерфейс определяется на стадии поиска маршрута
• структура интерфейса содержит указатели на функцию
• подобно protosw структуре
29. Уровень интерфейса (output)
Формирование канального заголовка (Ethernet заголовка) или
произведение специфических инкапсуляций (natm, ppp)
Может быть цепью виртуальных интерфейсов
Пакет готов к передаче в интерфейсную очередь
• hint: альтернативная организация очереди (ALTQ) происходит на
данном этапе
• необходим splnet() уровень
По факту вызова ifp -> if_start
• драйвер направляет пакет в кольцо передачи (TX DMA ring)
• Подобно всему драйвероспецифичному
• TX прерывание или иное подобное действие высвободит пакет сквозь
трансивер
32. Маршрутизация (обзор)
Форвард данные (forwarding info)
Адреса канального уровня в этом же дереве
Адресная группа (aka Address Family) независима
• каждая AF имеет отдельное дерево
«Там чудеса: там леший бродит, … »
• не там, чудеса начинаются здесь!
35. Маршрутизация (метки)
Маршрут может быть оттагированным ядром
Метка заменяется на integer в ядре
• мысли? (идея: сравнение строк) == скорость, память
pf «умеет» фильтровать или тагировать трафик на основе
rtlabels
bgpd может помечать маршруты посредством BGP path
атрибутов
ospfd в состоянии устанавливать метки маршрута,
основанные на внешних маршрутных тагах
36. Маршрутизация (домены)
Расширенная версия множественных маршрутных таблиц
• максимум 256 таблиц
• таблица по умолчанию – ID 0
Набору интерфейсов определяется специфическая таблица
• одна и та же сеть может быть определена множество раз
Маршрутный сокет маркирует сообщения различными ID
таблиц
37. Маршрутизация (приоритеты)
Маршрутные демоны должны содержать свои таблицы «в
чистоте» (синхронизированными)
Разные приоритеты на случай конфликтов
• OSPF более предпочтителен системой чем BGP
Синхронизация в userland – это тяжело
• «ресурсные войны»
• маршрутный сокет – потери
Необходимо лучшее решение
38. Маршрутизация (приоритеты)
Дополнительная маршрутная преференция – метрика
Каждый маршрутный источник имеет свой тип
• #define RTP_STATIC 8 /* static routes */
• #define RTP_OSPF 16 /* OSPF routes */
• #define RTP_RIP 24 /* RIP routes */
• #define RTP_BGP 32 /* BGP routes */
• иные
41. От теории к практике
Применение sysctl(8)
• kern.maxclusters управление mbuf(9)
• net.inet.ip.ifq.maxlen управление очередью
• net.inet.ip.maxqueue управление очередью
• net.inet.{tcp,udp}.{send,recv}space размер окна
• net.{inet,inet6}.{ip,ip6}.{forwarding,mforwarding}
В большинстве случаев достаточно системного
ifconfig(8) + pfctl(8)