Перейти к содержимому
Калькуляторы

Фильтр трафика Фильтр трафика

1 час назад, oleg_n сказал:

- добавлено удаление .... и финальных '/' в http uri....

А это зачем?

 

> curl -D- http://forum.nag.ru/uploads 
HTTP/1.1 301 Moved Permanently
Server: nginx/1.10.1
Date: Thu, 14 Sep 2017 16:52:10 GMT
Content-Type: text/html; charset=iso-8859-1
Content-Length: 236
Connection: keep-alive
Location: http://forum.nag.ru/uploads/
Cache-Control: max-age=86400
Expires: Fri, 15 Sep 2017 16:52:10 GMT

....

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

16 часов назад, vop сказал:

А это зачем?

 


> curl -D- http://forum.nag.ru/uploads 
HTTP/1.1 301 Moved Permanently
Server: nginx/1.10.1
Date: Thu, 14 Sep 2017 16:52:10 GMT
Content-Type: text/html; charset=iso-8859-1
Content-Length: 236
Connection: keep-alive
Location: http://forum.nag.ru/uploads/
Cache-Control: max-age=86400
Expires: Fri, 15 Sep 2017 16:52:10 GMT

....

 

 

 Вот именно за этим. В реестре были замечены записи, которые отличаются только финальным '/'. Т.о. уменьшаем дублирующие записи. Плюс, если в блоке есть http://some.domain, а пользователь вводит http://some.domain/, то, благодаря приведению к виду http://some.domain, мы блокируем этот запрос.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Надо данные нагрузочного тестирования. Так как libnfqueue копирует данные в пространство пользователя, сдается мне 10G там смысла не имеет даже трогать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

20 часов назад, StSphinx сказал:

Надо данные нагрузочного тестирования.

Пока точных данных не могу получить. Но примерные такие:

 

- xeon E5-2603, 8 ядер

- ~700Mb/s

- 6 очередей nfq

- LA ~1.5

 

Цитата

Так как libnfqueue копирует данные в пространство пользователя, сдается мне 10G там смысла не имеет даже трогать.

Это да, это печально. Но потрогать, если есть в наличии 10G всё таки можно. Если же не для теста, а для полноценной работы, то, думаю, лучше применять комбинированные схемы работы. Допустим, ipset'ом фильтровать ip'шники, потом оставшийся трафик ipset'ом направлять выборочно на фильтр.

 

Если будет время, хотел попробовать комбинированную схему такую:

 

- ipset'ом фильтровать ip

- domainset'ом(модуль, который надо сделать на основе ipset, который будет фильтовать чисто dns-запросы) фильтровать домены

- оставшийся трафик(остаётся http) выборочно по портам или по ip(ipset'ом) отправлять на фильтр уже

 

Это должна быть достаточно эффективная схема.

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

1 минуту назад, oleg_n сказал:

Пока точных данных не могу получить. Но примерные такие:

 

- xeon E5-2603, 8 ядер

- ~700Mb/s

- 6 очередей nfq

- LA ~1.5

 

Это да, это печально. Но потрогать, если есть в наличии 10G всё таки можно. Если же не для теста, а для полноценной работы, то, думаю, лучше применять комбинированные схемы работы. Допустим, ipset'ом фильтровать ip'шники, потом оставшийся трафик ipset'ом направлять выборочно на фильтр.

 

Если будет время, хотел попробовать комбинированную схему такую:

 

- ipset'ом фильтровать ip

- domainset'ом(модуль, который надо сделать на основе ipset, который будет фильтовать чисто dns-запросы) фильтровать домены

- оставшийся трафик(остаётся http) выборочно по портам или по ip(ipset'ом) отправлять на фильтр уже

 

Это должна быть достаточно эффективная схема.

 

 

 

Может проще было решить проблему кардинально - сменить engine обработки трафика на что-то типа dpdk/netmap ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

52 минуты назад, StSphinx сказал:

Может проще было решить проблему кардинально - сменить engine обработки трафика на что-то типа dpdk/netmap ?

ПО изначально делалось под требования заказчика в 1-2Gb/s. С этим оно справляется с запасом, свою задачу выполняет. Зачем сейчас, когда уже всё отлажено, что-то менять?

 

Насчёт простоты. По требуемому времени проще как раз реализовать комбинированную схему. Даже если вкладываться в написание аналога ipset для dns-фильтрации. Единственный момент - перечитывание конфига. trfl фильтрует трафик по текущему конфигу, перечитывая новый, и в конце этого процесса переключается на новый; т.о. нет моментов работы по неполному списку из-за этого(всегда в работе полный список - либо старый, либо новый). С ipset возможны пропуски в момент обновления конфига, даже при использовании rename(их будет, конечно, ничтожное кол-во, но всё же).

 

Насчёт кардинального. Мне как раз более кардинальным видится уход полностью в пространство ядра, что бы избежать копирования пакетов. Если делать красиво, то надо просто сделать недостающие модули для netfilter, что бы можно было использовать iptables/nftables. Для фильтрации ip есть ipset; для фильтрации доменов сделать на основе ipset модуль; для фильтрации tls сделать модуль; и для фильтрации http тоже придётся модуль сделать. Т.о. получим гибкую систему в связке с iptables/nftables, где админ правила создал, а rknr_get.pl какой-нибудь просто обновляет списки. Это был бы самый лучший вариант с точки зрения производительности(немного медленней dpdk/netmap, но с возможностью ставить в разрыв, интергацией с iptables и не зависимостью от сетевого оборудования). И не надо тащить trfl полностью монолитом в ядро.

 

По поводу dpdk. Я лично с ним не работал, поэтому такие манёвры достаточно затратны по времени. И есть же уже extfilter, если хочется dpdk. Люди пользуются. Работает хорошо, судя по отзывам.

 

Про netmap только услышал.

 

Мне лично что не оч. нравится в dpdk(у netmap, насколько успел понять, такая же особенность), что не все сетёвки поддерживаются. Это наверное не касается каналов в 10Gb/s, т.к. все серьёзные девайсы там поддерживаются, насколько я знаю, но всё же если взять за внимание, что с dpdk я не работал совсем, а модули ядра когда-то какие-то писал, плюс то, что модуль ядра для netfilter будет работать со всеми сетёвками, которые поддерживает linux, то проще замарочиться модулем ядра(при всех его минусах, вроде kernel panic).

Изменено пользователем oleg_n

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Даже в текущем коде ещё есть места, где можно подвыжать производительности:

 

- замутить свой аллокатор памяти для выделения большого куска памяти под разбор пакета, где все внутреннии malloc'и будут копошиться в этом куске;

  этот кусок не освобождается, а используется повторно для следующего пакета; в итоге будет, грубо, один malloc при старте trfl и ни одного malloc/free потом;

- почти тоже самое для конфига(добиваемся одной пары (free, malloc) при перечитывании конфига);

- интеграция с conntrack(может снизить существенно нагрузку)

 

Хз насколько результат этого всего можно будет ощутить на 10Gb/s :-).

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

1 час назад, oleg_n сказал:

- замутить свой аллокатор памяти для выделения большого куска памяти под разбор пакета, где все внутреннии malloc'и будут копошиться в этом куске;

  этот кусок не освобождается, а используется повторно для следующего пакета; в итоге будет, грубо, один malloc при старте trfl и ни одного malloc/free потом;

Должен быть такой уже в ядре.

Во фре точно есть спец аллокатор для таких вещей.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

2 минуты назад, Ivan_83 сказал:

Должен быть такой уже в ядре.

Во фре точно есть спец аллокатор для таких вещей.

Да, есть такой - slab. Но оно только для выделения памяти в пространстве ядра.

Изменено пользователем StSphinx

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

51 минуту назад, Ivan_83 сказал:

Должен быть такой уже в ядре.

Во фре точно есть спец аллокатор для таких вещей.

Не скажу, что я сильно упорно искал, но не слышал о таком для пространства пользователя(хотя нужон бывает очень для подобных задач). Может и есть какой заброшенный проект на github, но чем копаться в подобном, если есть, проще свой написать - по моим прикидкам там ничего особенного не будет. Список блоков с большими кусками, внутри которых подобные списки с маленькими кусками(на каждый malloc в коде). Главное, что бы всё это богатство было выравнено правильно для быстрого доступа. Но тут вся надежда на умолчальное выравнивание указателей.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

17 часов назад, oleg_n сказал:

Не скажу, что я сильно упорно искал, но не слышал о таком для пространства пользователя(хотя нужон бывает очень для подобных задач). Может и есть какой заброшенный проект на github, но чем копаться в подобном, если есть, проще свой написать - по моим прикидкам там ничего особенного не будет. Список блоков с большими кусками, внутри которых подобные списки с маленькими кусками(на каждый malloc в коде). Главное, что бы всё это богатство было выравнено правильно для быстрого доступа. Но тут вся надежда на умолчальное выравнивание указателей.

В Glib есть аллокатор, который выделяет память пулами с равными участками. Очень удобная штука.

https://developer.gnome.org/glib/2.42/glib-Memory-Slices.html

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

1 час назад, StSphinx сказал:

В Glib есть аллокатор, который выделяет память пулами с равными участками. Очень удобная штука.

https://developer.gnome.org/glib/2.42/glib-Memory-Slices.html

О. glib. Давно её не пользовал. Хорошая штука. Конкретно эти функции немного не то, что мне надо. Найду время, покажу о чём я говорю.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

2 часа назад, StSphinx сказал:

В Glib есть аллокатор, который выделяет память пулами с равными участками.

ИМХО тот же jmalloc имеет всякие фичи внутри для подобных ситуаций, просто оно отдельно не вынесено.

Нужно брать доки и читать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

30 минут назад, Ivan_83 сказал:

ИМХО тот же jmalloc имеет всякие фичи внутри для подобных ситуаций, просто оно отдельно не вынесено.

Нужно брать доки и читать.

 

jmalloc можно тоже поковырять. Мне нужна достаточно простая вещь и быстрая. glib Memory Slices крутая вещь, но для данной задачи выглядит overkill'ом, плюс бонусом будет зависимость проекта от glib. Хотелось бы без необходимости не добавлять новых зависимостей.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

6 часов назад, oleg_n сказал:

плюс бонусом будет зависимость проекта от glib

Зависимость от glib - это меньшая из забот. Любой мало-мальски сложный проект его использует. Можно воспринимать glib как libc 2.0 :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

15 часов назад, MATPOC сказал:

 Любой мало-мальски сложный проект его использует.

Ну, это не является техническим основанием для использования :-). Мало ли кто там что использует. Я не против glib, более того я за и использовал её в некоторых проектах, но конкретно в этом случае вижу следующие технические моменты:

 

- glib такая штука, что если её использовать, то проще во всём проекте(у неё свои типы и т.п.);

- тащить же зависимостью немаленькую glib ради одного функционала как-то не обосновано;

- если же очень хочется этот один функционал, то архитектурно правильней, imho, будет в этом случае вырезать нужную её часть(Memory Slices) и вставить в проект(хотя это связано с возможным гемором поддержки данного кода в синхронизированном состоянии с upstream);

- кроме всего, как я уже говорил, Memory Slices это overkill(и не совсем то) в данном случае, что делает использование glib только ради этого вообще бессмысленным.

 

Изменено пользователем oleg_n

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

  Коллеги, появилась новая мысль по поводу путей оптимизации. Думаю, можно обойтись одним ядерным модулем и оставить trfl почти как есть, но при этом увеличить производительность в несколько раз(до 10 раз!).

  Мысль такая. Нужен модуль, который будет на основе списка соединений выставлять 3 разных метки пакетам(new, accept, drop). Далее по цепочке iptables:

- если mark соответствует drop, то пакет отбрасываем(или перенаправляем куда надо);

- если mark соответствует accept, то accept;

- если mark соответствует new, то пакет уходит в trfl через nfqueue, где, если уже ясно каков вердикт, через какой-нибудь netlink модулю ядра отсылается инфа по данному соединению(accept или drop).

 

Такая схемка:

-->МОДУЛЬ--->NFQUEUE---->
     ^           ^
     |           |PKT
     |CONNMARK   v
    +----------------+
    |      TRFL      |
    +----------------+

 

Описаные ранее варианты, с выборочным направлением пакетов с помощью ipset в nfqueue тоже можно использовать дополнительно.

Изменено пользователем oleg_n

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Во всем этом мне видится одна фундаментальная проблема - как отделять мух от котлет? То есть, если web ходит не по стандартным портам?

Или я что-то упустил? А, нет, даже две:) Вторая это сборка сессий. Кто мешает пользователю выставить MTU для определенного типа трафика по-меньше, со всеми вытекающими.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

43 минуты назад, StSphinx сказал:

Во всем этом мне видится одна фундаментальная проблема - как отделять мух от котлет? То есть, если web ходит не по стандартным портам?

Или я что-то упустил? А, нет, даже две:) Вторая это сборка сессий. Кто мешает пользователю выставить MTU для определенного типа трафика по-меньше, со всеми вытекающими.

Описаная схема, как и используемая сейчас, подразумевает прохождение _всего_ трафика через фильтр(стандартные порты, нестандартные - не важно). Сборкой сессий будет заниматься trfl с помощью conntrack.

 

Если же в разрезе выборочного отвода трафика с помощью ipset говорить про удовлетворение РКН, то хватит отправления через nfqueue ip/портов, которые фигурируют в реестре.

 

43 минуты назад, StSphinx сказал:

Кто мешает пользователю выставить MTU для определенного типа трафика по-меньше, со всеми вытекающими.

Если речь про фрагментацию ip, то тут сборкой целого ip-пакета занимается ядро. Нам этим заморачиваться не надо.

Изменено пользователем oleg_n

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

10 минут назад, oleg_n сказал:

Если речь про фрагментацию ip, то тут сборкой целого ip-пакета занимается ядро. Нам этим заморачиваться не надо.

Скорее имеется в виду сборка TCP сессии.

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

3 минуты назад, max1976 сказал:

Скорее имеется в виду сборка TCP сессии.

 

Ну, тогда как и писал - trfl + conntrack(libnetfilter_conntrack) сессии собирает для вынесения решения, а модуль ядра этим сессиям уже выставляет метки в соответствии с принятыми решениями.

Изменено пользователем oleg_n

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Накидал тут по-быстрому общий вид аллокатора:

 

#ifndef __MEMPILE__H__
#define __MEMPILE__H__


#include "list.h"

struct mempile_piece {
        unsigned int size;
};

struct mempile {
        struct list_item_head list;
        unsigned int size;
        unsigned int size_free;
        void *child_next;
};

struct mempile* mempile_make(unsigned int size_def);
void* mempile_palloc(struct mempile *mempile, unsigned int size);
void mempile_reset(struct mempile *mpile);

#endif  /* __MEMPILE__H__ */

 

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "mempile.h"


static void* _mempile_padd(struct mempile *mpile, unsigned int size);


static unsigned int
_sizeof_aligned(unsigned int size)
{
        unsigned int rem;

        rem = size % sizeof(void*);
        if (rem)
                size += sizeof(void*) - rem;
        return size;
}

static void*
_ptr_aligned(void *ptr)
{
        unsigned int rem;

        rem = (unsigned long long int)ptr % sizeof(void*);
        if (rem)
                ptr += sizeof(void*) - rem;
        return ptr;
}
  
struct mempile*
mempile_make(unsigned int size)
{
        struct mempile *mpile;

        mpile = malloc(size + _sizeof_aligned(sizeof(*mpile)));
        if (!mpile)
                return NULL;

        memset(mpile, 0, sizeof(*mpile));
        list_item_head_init(&mpile->list);
        mpile->size = size;
        mpile->size_free = size - _sizeof_aligned(sizeof(struct mempile_piece));
        mpile->child_next = (void*)mpile + _sizeof_aligned(sizeof(*mpile));

        return mpile;
}

void*
mempile_palloc(struct mempile *mempile, unsigned int size)
{
        struct list_item_head *lh;
        struct mempile *mpile, *mpile_last;

        list_for_each(lh, &mempile->list) {
                mpile = list_item(lh, struct mempile, list);
                if (mpile->size_free >= size)
                        return _mempile_padd(mpile, size);
        }
        mpile_last = mpile;
        if (size > mempile->size)
                mpile = mempile_make(size +
                  _sizeof_aligned(sizeof(struct mempile_piece)));
        else
                mpile = mempile_make(mempile->size);
        if (!mpile)
                return NULL;
        list_add(&mpile->list, &mpile_last->list);
        return _mempile_padd(mpile, size);
}
  
static void*
_mempile_padd(struct mempile *mpile, unsigned int size)
{
        struct mempile_piece *mpile_piece;
        unsigned int s;

        if (mpile->size_free < size)
                abort();
        mpile_piece = mpile->child_next;
        memset(mpile_piece, 0, sizeof(*mpile_piece));
        list_item_head_init(&mpile_piece->list);
        mpile_piece->size = size;

        mpile->child_next = _ptr_aligned(mpile->child_next +
          _sizeof_aligned(sizeof(*mpile_piece)) + size);
        s = mpile->child_next - (void*)mpile_piece;
        if (s < mpile->size_free)
                mpile->size_free -= s;
        else
                mpile->size_free = 0;

        return mpile_piece + _sizeof_aligned(sizeof(*mpile_piece));
}

void
mempile_reset(struct mempile *mempile)
{
        struct list_item_head *lh;
        struct mempile *mpile, *mpile_last;

        list_for_each(lh, &mempile->list) {
                mpile = list_item(lh, struct mempile, list);
                mpile->size_free = mpile->size - _sizeof_aligned(sizeof(struct mempile_piece));
                mpile->child_next = (void*)mpile + _sizeof_aligned(sizeof(*mpile));
        }
}

 

Используется как-то так:

struct mempile *mpile;
char *str;

mpile = mempile_make(4 * 1024 * 1024);
if (!mpile)
  exit(1);
str = mempile_palloc(mpile, 1000);
if (!str)
  exit(1);

...
/* рабочий цикл закончен, сбрасываем выделенные куски для нового использования */
mempile_reset(mpile);
str = mempile_palloc(mpile, 1000);
if (!str)
  exit(1);
...

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Извиняюсь, неточно выразился. Имелось ввиду TCP MSS.

По поводу кода - само собой каждый C-шник должен написать свой аллокатор, но наверное лучше взять что-то проверенное временем?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

2 часа назад, StSphinx сказал:

По поводу кода - само собой каждый C-шник должен написать свой аллокатор, но наверное лучше взять что-то проверенное временем?

Лучше. Но где? Мне нужен аллокатор для разбора пакета, упрощёно, выделяющий большой кусок памяти сразу и все последующие выделения осуществляющий внутри этого куска.

Код, который я выше выложил, несмотря на то, что это эскизный вариант, _уже_ справляется с этой задачей. И он специфичный имено для данного случая, стало быть лишён каких-либо излишеств в функционале, которые будут отрицительно влиять на производтельность, и его намного проще оптимизировать, чем тот же glib Memory Slices.

 

Хотя, при достаточном времени, в идеале, конечно, сделать и так и так и померять производительность.

Изменено пользователем oleg_n

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

oleg_n подскажите я по коду так  и не понял.
Как осуществляется блокировка https? По IP или по server name?

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Гость
Ответить в тему...

×   Вставлено в виде отформатированного текста.   Вставить в виде обычного текста

  Разрешено не более 75 смайлов.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставить изображения напрямую. Загрузите или вставьте изображения по ссылке.