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

| 1 Comment
Порядок загрузки модулей, осуществляющих фильтрацию пакетов, определяет, в каком порядке будут проходить пакеты через них (это определяется тем, когда модуль зарегистрировал хук у 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 - для исходящего

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



1 Comment

Классно; это полезная информация, многим может пригодиться.

Leave a comment

About this Entry

This page contains a single entry by Vsevolod Stakhov published on October 24, 2007 6:29 PM.

Немного о mercurial was the previous entry in this blog.

Встроенные функции gcc для атомарных операций is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.

Categories

OpenID accepted here Learn more about OpenID