Showing posts with label FreeBSD. Show all posts
Showing posts with label FreeBSD. Show all posts

Tuesday, May 4, 2010

Ng_multicar или шейпинг трафика для большого числа ip адресов

Обычный ng_car довольно удобно использовать для шейпинга небольшого количества отдельных полос, в случае же увеличения количества полос поиск хука, в который будет отправлено правило будет занимать довольно много времени, т.к. для этого используется линейный список (ng_ipfw.c):


/* Look up hook by name */
hook_p
ng_ipfw_findhook(node_p node, const char *name)
{
u_int16_t n; /* numeric representation of hook */
char *endptr;

n = (u_int16_t)strtol(name, &endptr, 10);
if (*endptr != '\0')
return NULL;
return ng_ipfw_findhook1(node, n);
}

/* Look up hook by rule number */
static hook_p
ng_ipfw_findhook1(node_p node, u_int16_t rulenum)
{
hook_p hook;
hpriv_p hpriv;

LIST_FOREACH(hook, &node->nd_hooks, hk_hooks) {
hpriv = NG_HOOK_PRIVATE(hook);
if (NG_HOOK_IS_VALID(hook) && (hpriv->rulenum == rulenum))
return (hook);
}

return (NULL);
}


Кроме этого, такое использование ng_car подходит только для ipfw с его ng_ipfw. В моем случае для pf'а необходимо направлять весь трафик в netgraph ноду (например, с ng_ether), поэтому задача разделения полос и ip адресов возлагается целиком на netgraph модуль. Возникли следующие идеи по реализации такого шейпера:

  • Использовать для хранения информации о полосе для ip judy массив для ускорения поиска

  • Для загрузки и выгрузки информации о полосах и соответствующих им ip использовать либо отдельные команды, либо парсить некоторый загрузочный файл


  • Поддерживать создание динамических полос для сетей, например, если аргумент - сеть в CIDR формате, то создавать полосу для ip по получению пакета от данного ip из данной сети (нечто вроде динамических пайпов dummynet'а)


При такой схеме при увеличении числа пользователей нагрузка на роутер не должна значительно возрастать.

Friday, January 16, 2009

Даунгрейд FreeBSD 8 (CURRENT) до 7-STABLE

В ходе даунгрейда выплыла проблема с невозможностью установить мир. Установка затыкалась на mtree, который не мог работать с libc от семерки. Проблема решилась ручной установкой usr.bin/find и usr.sbin/mtree набрав в этих директориях make install. Также stass@ посоветовал способ с исправлением таргета bootstrap-tools из src/Makefile1.inc. Этот способ я не пробовал, но он тоже должен работать. После установки find и mtree, installworld работает нормально. Система, обновленная таким образом тоже нормально работает.

Thursday, November 8, 2007

Извлечение данных из sysctl (3) в FreeBSD

С системным вызовом sysctl есть одна проблема: нет очевидного способа узнать тип возвращаемого значения. Это неважно, когда тип мы точно знаем, но сильно затрудняет жизнь, когда не знаем. Получается тип следующим образом:
к mib-у приписываются с начала два дополнительных целых - 0,4, то есть, целевой миб будет выглядеть так:
0,4,[mib]
где mib - искомый mib
Далее делается sysctl для этого mib-а, размер лучше задать равным BUFSIZ или же получить размер, указав в качестве буфера NULL.
Чтобы узнать, что хранится в mib'е необходимо разобрать полученный буфер следующим образом:
(unsigned int) (char) [(char) [(char)...]]
Для извлечения значения первое поле можно не использовать, но в нем также хранится тип, который извлекается бинарным "И" с константой CTLTYPE (number & CTLTYPE). Полученное значение сравнивается с CTL константами в /usr/include/sys/sysctl.h. Два символа после этого значения также можно использовать для опеределения типа - 1-й символ определяет тип, второй символ (которого может не быть) определяет наличие знака для численных типов. Соответствие типов и символов типа:
int - 'I'
long - 'L'
quad_t - 'Q'
pointer - 'P'
typedef - 'T'
struct - 'S'
Для 'S' и 'T' типов тип описывается после запятой, то есть, строчка будет 'S,some_struct' или 'T,some_type_t'. Для численных типов второй символ может быть 'U', тогда это число - беззнаковое число.

Wednesday, October 24, 2007

Определение порядка прохождения пакетов по различным пакетным фильтрам в FreeBSD

Порядок загрузки модулей, осуществляющих фильтрацию пакетов, определяет, в каком порядке будут проходить пакеты через них (это определяется тем, когда модуль зарегистрировал хук у pfil'a).
Как происходит передача пакета хуку pfil'om:
for (pfh = pfil_hook_get(dir, ph);
        pfh != NULL;
        pfh = TAILQ_NEXT(pfh, pfil_link)) {
            if (pfh->pfil_func != NULL) {
                rv = (*pfh->pfil_func)(pfh->pfil_arg, &m, ifp, dir, inp);
                if (rv != 0 || m == NULL)
                       break;
            }  
}
То есть, перебираются все хуки, начиная с TAILQ_FIRST и им передается пакет в качестве параметра.
Если пакет дропается, то дальше по цепочке он не передается (это надо помнить при записи данных о дропнутых пакетах в лог).
Регистрируются хуки таким образом:
    /*
     * insert the input list in reverse order of the output list
     * so that the same path is followed in or out of the kernel.
     */
    if (flags & PFIL_IN)
        TAILQ_INSERT_HEAD(list, pfh1, pfil_link);
    else
        TAILQ_INSERT_TAIL(list, pfh1, pfil_link);
То есть, для входящего трафика модуль, добавивший свой хук последним, будет обрабатывать пакеты первым, и наоборот - для исходящего модуль, добавивший свой хук первым будет обрабатывать пакеты первым.
Теперь надо определиться с порядком загрузки модулей в ядро:
модули при загрузке присоединяются к определенным подсистемам в ядре: эти подсистемы описаны в файле sys/kernel.h:
нас интересуют
    SI_SUB_PROTO_IF     = 0x8400000,    /* interfaces*/
    SI_SUB_PROTO_DOMAIN = 0x8800000,    /* domains (address families?)*/
    SI_SUB_PROTO_IFATTACHDOMAIN = 0x8800001,    /* domain dependent data init*/
Системы загружаются по очереди, то есть, чем меньше номер, тем раньше загрузится подсистема и тем раньше к ней присоединится модуль. Сам модуль может присоединиться к подсистеме, указывая ряд различных позиций (согласно аргументу order в функции DECLARE_MODULE):
enum sysinit_elem_order {
    SI_ORDER_FIRST      = 0x0000000,    /* first*/
    SI_ORDER_SECOND     = 0x0000001,    /* second*/
    SI_ORDER_THIRD      = 0x0000002,    /* third*/
    SI_ORDER_MIDDLE     = 0x1000000,    /* somewhere in the middle */
    SI_ORDER_ANY        = 0xfffffff /* last*/
};
Таким образом, смотрим на подсистему и порядок подключения к подсистеме у каждого интересующего модуля:
pf:
DECLARE_MODULE(pf, pf_mod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_FIRST);
ipfw:
DECLARE_MODULE(ipfw, ipfwmod, SI_SUB_PROTO_IFATTACHDOMAIN, SI_ORDER_ANY);
ipfilter:
DECLARE_MODULE(ipfilter, ipfiltermod, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY);

Согласно порядку загрузки подсистем, имеем  SI_SUB_PROTO_DOMAIN -> SI_SUB_PROTO_IFATTACHDOMAIN (ipfilter -> (ipfw, pf)), согласно порядку присоединения модулей к подсистеме имеем окончательный порядок загрузки модулей: ipfilter -> pf -> ipfw.
Таким образом, для входящего трафика:
ipfw -> pf -> ipfilter -> stack
для исходящего:
stack -> ipfilter -> pf -> ipfw

С другой стороны, pf часто загружают в качестве kld, а не компилируют в ядро статически, тогда, если kldstat показывает модуль pf.ko, то загрузился он после ipfw, и порядок прохождения пакетов тоже изменился:
pf -> ipfw -> ipfilter -> stack - для входящего
stack -> ipfilter -> ipfw -> pf - для исходящего

Эта деталь очень важна, особенно при составлении правил, включающих трансляцию адресов, так как фильтру, находящемуся дальше в цепочке приходит уже модифицированный пакет.



Tuesday, October 23, 2007

ng_fixttl - netgraph модуль, исправляющий ttl пакетов

Данный модуль создавался в целях обеспечения простенькой защиты от использования nat-роутеров пользователями сети. Естественно, обходится данная защита очень просто, но со своей задачей она-таки справляется. У нас ng_fixttl используется совместно с ng_ipfw:

ipfw add netgraph 2 ip from any to $host

При этом необходимо помнить о sysctl net.inet.ip.fw.one_pass, который определяет, будет ли правило netgraph эквивалентно allow, или же пакет будет возвращаться в ipfw (на следующее за правилом netgraph правило), или же сразу же будет отдаваться сетевому стеку (если эта переменная установлена в 1). 

ng_fixttl.tar.gz

Настройка gmirror в FreeBSD

Последовательность операций такова (FreeBSD >= 5.3 ):
1. Инсталируем FreeBSD
2. Ребутимся,загружаясь вновь с install CD
3. Переходим в fixit mode (для FreeBSD < 5.4 нужен CD2)
4. Выполняем следующие операции:
# chroot /dist
# mount_devfs devfs /dev
# gmirror load
# gmirror label -v -b round-robin gm0 /dev/ad0
# gmirror insert gm0 /dev/ad1
# mount /dev/mirror/gm0s1a /mnt
# echo ‘geom_mirror_load=”YES”‘ >> /mnt/boot/loader.conf
5. Правим fstab (sed или ed)
# sed “s,ad0,mirror/gm0,” /mnt/etc/fstab > /mnt/etc/fstab.new
# mv /mnt/etc/fstab.new /mnt/etc/fstab
6. Ребутимся, загружаясь с жесткого диска
7. Ждем некоторое время, пока mirror просинхронизируется (минимальная установка в 100 Mb синхронизировалась минут 15).
8. Вынимаем любой из дисков; наслаждаемся :)