netgraph нода: ng_patch
Автор новости: Vadim Goncharov (nuclight)
netgraph — модульная сетевая подсистема ядра FreeBSD, основанная на принципе графов. В Netgraph строится граф из узлов различных типов, узел каждого типа имеет некоторое количество входов/выходов (далее хуков (hooks)). Узел netgraph позволяет производить определенные действия над пакетом, проходящим через него.
netgraph нода: ng_patch
С помощью ng_patch появилась возможность редактирования произвольных полей в заголовках IP-пакетов.
Эта нода позволяет производить последовательность операций над произвольными байтами пакета, не только ToS или TTL — как в данных, так и в заголовке, причем, все это в ядре.
Вместе с модулем ng_bpf это дает возможность выловить и изменить любой пакет.
Возможности открываются огромные.
Например, в icq пакете можно изменять текст «перейдите по ссылке» на «Перейдя по ссылке можна сфватить вирусню » =)
Maxim Ignatenko написал ноду ng_patch(4) — и её, наконец, закоммиттили, а недавно смержили в 8-STABLE (r209843) и вчера — в 7-STABLE (r210019). Работает также на 6.4, если убрать в коде CSUM_SCTP.
В настоящее время ng_patch — единственный штатный способ менять в проходящем пакете что-то. Зато, в отличие от других решений (в том числе на других ОС), эта нода позволяет производить последовательность операций над произвольными байтами пакета, не только ToS или TTL.
Пример для iptables:
1 2 | iptables -t mangle -A POSTROUTING -j TOS --set-tos 0x10 iptables -t mangle -A POSTROUTING -j TTL --ttl-set 128 |
В мане рассмотрены примеры изменения ToS и TTL.
Тут будет описано изменение DSCP.
DSCP(Differenciated Services Code Point)— поле в IP-пакете, позволяющее назначить сетевому трафику различные уровни обслуживания. Для достижения этого каждый пакет в сети помечается кодом DSCP и соответствующим ему уровнем обслуживания.
Аббревиатуры ToS и DSCP родственны — используются для обозначения специального байта данных стандартного заголовка IP-пакета. Этот байт несет информацию о приоритете трафика, который в бизнес-трафике обычно назначается для пакетов IP-телефонии (третий сетевой уровень L3). Поскольку этот один и тот же байт иногда интерпретируется по-разному (либо как ToS байт, либо как DS/DSCP байт), получается некоторая путаница, хотя смысл и принцип технологии приоритезации не меняется — пакеты, помеченные высоким более приоритетом, передаются быстрее (менее приоритетные становятся в очередь). На рисунке показано расположение бит указанного байта (красным помечена наиболее важная, серым — неиспользующаяся часть).
Когда используют терминологию ToS, то в контексте приоретизации имеют в виду 3 старшие бита P2..P0, кодирующие уровень приоритета от 0 (минимальный приоритет) до 7 (максимальный приоритет). Для IP-телефонии применяется уровень приоритета 5 (critical, ToS-байт равен 0xA0 или 10100000b), а для обычного трафика уровень 0 (routine, ToS-байт равен 0×00 или 00000000b). У Cisco есть для каждого уровня приоритета специальное имя (precedence critical, precedence flash и т. д., см. таблицу).
IP Precedence Value
| Уровень |
Имя |
| 0 | routine |
| 1 | priority |
| 2 | immediate |
| 3 | flash |
| 4 | flash-override |
| 5 | critical |
| 6 | internet |
| 7 | network |
Когда используют терминологию DSCP, имеются в виду 6 старших бит DS5..DS0, где DS5..DS3 кодируют уровень класса обслуживания от 0 (минимальный приоритет) до 7 (максимальный приоритет) и приоритет удаления (от 0, когда приоритет удаления максимальный, до 7, когда приоритет удаления минимальный — кодирование приоритета удаления «обратное»). В итоге получается число от 0 до 63, кодирующее приоритет (чем больше число, тем трафик важнее). Такое многоуровневое кодирование приоритета часто оказывается избыточным, и поэтому используются только биты DS5..DS3. При IP-телефонии применяется класс сервиса 5 (DS-байт равен 0xA0 или 10100000b), а для обычного трафика класс сервиса 0 (DS-байт равен 0×00 или 00000000b). Сравните с ToS — изменилась только терминология, а значение байта передается то же самое.
Для ng_patch:
Байты пакета для операций рассматриваются как без знаковые целые длиной 1, 2, 4 или 8 байт, применять можно такие операции из языка Си: присвоение нового значения (=), добавление (+=), вычитание (-=), умножение (*=), деление (/=), отрицание (= -), побитовое AND (&=), побитовое OR (|=), побитовое XOR (^=), сдвиг влево (<<=), сдвиг вправо (>>=). Исключением является отрицание, здесь данные рассмтариваются как знаковые, а аргумент не используется.
Конфигурируется нода следующими структурами, ниже рассмотрим на примере:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | struct ng_patch_op { uint64_t value; uint32_t offset; uint16_t length; /* 1,2,4 or 8 bytes */ uint16_t mode; }; /* Patching modes */ #define NG_PATCH_MODE_SET 1 #define NG_PATCH_MODE_ADD 2 #define NG_PATCH_MODE_SUB 3 #define NG_PATCH_MODE_MUL 4 #define NG_PATCH_MODE_DIV 5 #define NG_PATCH_MODE_NEG 6 #define NG_PATCH_MODE_AND 7 #define NG_PATCH_MODE_OR 8 #define NG_PATCH_MODE_XOR 9 #define NG_PATCH_MODE_SHL 10 #define NG_PATCH_MODE_SHR 11 struct ng_patch_config { uint32_t count; uint32_t csum_flags; struct ng_patch_op ops[]; }; |
Для примера в проходящих пакетах выставляем DSCP в AF33. В RFC 791 начало заголовка IP:
1 2 3 4 5 | 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Version| IHL |Type of Service| Total Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
Нужный нам 1 байт — ToS — второй, то есть, имеет смещение 1 от начала заголовка. Мы уже знаем, что байт ToS был переопределен под использование DSCP, в RFC 3168 он определяется так:
1 2 3 4 5 6 7 | 0 1 2 3 4 5 6 7 +-----+-----+-----+-----+-----+-----+-----+-----+ | DS FIELD, DSCP | ECN FIELD | +-----+-----+-----+-----+-----+-----+-----+-----+ DSCP: differentiated services codepoint ECN: Explicit Congestion Notification |
Нужный нам AF33 описан в RFC 2597 (и в справочнике по цискам — тоже): AF33 = ’011110′.
Что теперь? А вот 2 бита ECN нам трогать не надо, надо их оставить как есть. Минимальный же размер для операции — 1 байт. Сначала надо обнулить искомые биты, а потом — сделать из них нужное нам значение. Два шага, сначала:
1 2 3 4 5 6 7 8 9 10 11 | +-+-+-+-+-+-+-+-+ |s t u v w x y z| исходный байт +-+-+-+-+-+-+-+-+ AND +-+-+-+-+-+-+-+-+ |0 0 0 0 0 0 1 1| значение 0x03 +-+-+-+-+-+-+-+-+ = +-+-+-+-+-+-+-+-+ |0 0 0 0 0 0 y z| +-+-+-+-+-+-+-+-+ |
потом:
1 2 3 4 5 6 7 8 9 10 11 | +-+-+-+-+-+-+-+-+ |0 0 0 0 0 0 y z| промежуточный байт +-+-+-+-+-+-+-+-+ OR +-+-+-+-+-+-+-+-+ |0 1 1 1 1 0 0 0| значение AF33, сдвинутое на 2 влево: 0x78 (7=0111, 8=1000) +-+-+-+-+-+-+-+-+ = +-+-+-+-+-+-+-+-+ |0 1 1 1 1 0 y z| то, что надо! +-+-+-+-+-+-+-+-+ |
Итак, составляем структурки для конфигурации узла и получаем итоговые команды (это уже полная конфигурация):
1 2 3 4 5 6 7 8 | /usr/sbin/ngctl -f- <<-SEQ mkpeer ipfw: patch 600 in name ipfw:600 dscp_af33 msg dscp_af33: setconfig { count=2 csum_flags=1 ops=[ \ { mode=7 value=0x03 length=1 offset=1 } \ { mode=8 value=0x78 length=1 offset=1 } ] } SEQ /sbin/ipfw add 160 netgraph 600 ip from any to any not dst-port 80 |
Операций у нас две, и поэтому count сообщает, что в массиве ops=[ ... ] будет две структуры ng_patch_op. Отдельно осталось рассмотреть поле csum_flags — оно может принимать такие значения (см.
1 2 3 4 | #define CSUM_IP 0x0001 /* will csum IP */ #define CSUM_TCP 0x0002 /* will csum TCP */ #define CSUM_UDP 0x0004 /* will csum UDP */ #define CSUM_SCTP 0x0040 /* will csum SCTP */ |
Поле инструктирует ноду сообщить IP-стеку о необходимости пересчитать контрольную сумму. Сделано это будет позже, не в самой ноде. В данном примере дается команда пересчитать контрольную сумму только IP-заголовка, другие не трогать.
Только учтите, что это средство — в стиле Unix, и позволяет админу прострелить себе ногу. Хитро поигравшись значениями, можно так изменить пакет, что на выходном пути IP-стека ядро свалится в панику или будут какие-нибудь еще непредсказуемые результаты.
