Модификация ToS/DSCP/TTL/etc. на FreeBSD: ng_patch [2010]
Патчи, позволяющие матчить и изменять ToS/DSCP на FreeBSD, ходят в разных вариантах по сети уже лет шесть, вот очередная инкарнация, например. К сожалению, ни один из них так и не попал в базовую систему, хотя пример по ссылке, скажем, для конкретной задачи удобнее того, что описано ниже. Даже возникает подозрение, что это всё потому, что предыдущие решения были недостаточно общие :) Вот Maxim Ignatenko написал ноду ng_patch(4) - и её, наконец, закоммиттили, а недавно смержили в 8-STABLE (r209843) и вчера - в 7-STABLE (r210019). Работает также на 6.4, если убрать в коде CSUM_SCTP, я проверял. В настоящее время ng_patch - единственный штатный способ поменять что-то в проходящем пакете. Зато, в отличие от других решений (в том числе на других ОС), эта нода позволяет производить последовательность операций над произвольными байтами пакета, не только ToS или TTL. В мане рассмотрены примеры изменения ToS и TTL, я же расскажу чуть подробнее, с примером для DSCP. Байты пакета для операций рассматриваются как беззнаковые целые длиной 1, 2, 4 или 8 байт, применять можно такие операции из языка Си: присвоение нового значения (=), добавление (+=), вычитание (-=), умножение (*=), деление (/=), отрицание (= -), побитовое AND (&=), побитовое OR (|=), побитовое XOR (^=), сдвиг влево (<<=), сдвиг вправо (>>=). Исключением является отрицание, здесь данные рассмтариваются как знаковые, а аргумент не используется. Конфигурируется нода следующими структурами, ниже рассмотрим на примере:
Нужный нам байт (ровно один байт) - ToS - второй, то есть, имеет смещение 1 от начала заголовка. Далее, мы знаем, что байт ToS был переопределен под использование DSCP, в RFC 3168 он определяется так:
Нужный нам AF33 описан в RFC 2597 (впрочем, в любом справочнике по цискам и т.п. - тоже): AF33 = '011110'. Что теперь? А вот 2 бита ECN нам трогать не надо, надо их оставить как есть. Минимальный же размер для операции - 1 байт. На помощь спешитКапитан Очевидность булева алгебра и прочая двоичная логика с дискретной математикой - применением очень сложных операций И и ИЛИ сначала надо обнулить искомые биты, а потом - сделать из них нужное нам значение. Два шага, сначала:
+-+-+-+-+-+-+-+-+
|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|
+-+-+-+-+-+-+-+-+
а потом:
+-+-+-+-+-+-+-+-+
|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| то, что надо!
+-+-+-+-+-+-+-+-+
Итак, составляем структурки для конфигурации узла и получаем итоговые команды (это уже полная конфигурация):
/usr/sbin/ngctl -f- <<-SEQ
mkpeer ipfw: patch 600 in
name ipfw:600 dscp_af33
msg dscp_af33: setconfig { count=2csum_flags=1 ops=[ \
{ mode=7value=0x03length=1offset=1 } \
{ mode=8value=0x78length=1offset=1 } ] }
SEQ
/sbin/ipfw add 160 netgraph 600 ip from any to any not dst-port 80
Как можно догадаться, в командах и по тексту одинаковым цветом выделены одинаковые значения, для удобства сопоставления читателем. Операций у нас две, и поэтому count сообщает, что в массиве ops=[ ... ] будет две структуры ng_patch_op. Отдельно осталось рассмотреть полеcsum_flags - оно может принимать такие значения (см. <sys/mbuf.h>), которые можно OR-ить:
#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-заголовка, другие не трогать.
Надеюсь, вышеприведенной информации достаточно, чтобы самостоятельно менять в пакетах что угодно. NB: Только учтите, что это средство - в стиле Unix, и позволяет админу прострелить себе ногу. Хитро поигравшись значениями, можно так изменить пакет, что на выходном пути IP-стека ядро свалится в панику или будут какие-нибудь еще непредсказуемые результаты.
Да, быть может, этот способ не очень дружелюбен, если нужно поменять только TOS или TTL, зато он позволяет делать куда больше вещей, и это единственный штатный способ на текущий момент.