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'а)


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

OpenID и wordpress

Плагин для работы openid аутентификации в wordpress работает достаточно странно: он требует для парсинга XRDS xml парсер, но то, как он пытается его загрузить, довольно ужасно:

if (!extension_loaded($name)) {
foreach ($params['libname'] as $libname) {
if (@dl($libname)) {
$classname = $params['classname'];
}
}
} else {

То есть, при отсутствии заданного расширения, он пытается его dlopen'уть, но при этом игнорируя любые ошибки. В итоге ошибка dl становится фатальной, но нигде не отображается. Опытным путем выяснилось, что для работы плагина необходимо расширение dom.so (textproc/php5-dom). Сейчас openid авторизация тут должна работать.

Tuesday, April 27, 2010

Rspamd и xml

Замучавшись бороться с lex+yacc решил перевести конфигурацию rspamd в xml формат. Минусы старой системы довольно прозаичны: lex при переключении внутренних состояний парсера (lex states) не умеет при yyrestart'е переключаться в INITIAL state, что приводит к невозможности перечитывания конфига "на лету". Кроме этого, сами по себе lex+yacc предоставляют слишком много возможностей для генерации грамматик, что само по себе неплохо, но я ловлю себя на мысле, что bind like конфиг-файл зачастую не очень очевиден для пользователя, яркий пример, когда переменные rspamd на самом деле являются не переменными в полном понимании этого слова, а подстановками текста. Также такой конфиг крайне сложно парсить чем-то, отличным от оригинальной lex/yacc грамматики. Моя же идея была в расширении интерфейса управления кластера rspamd, давая возможность конфигурации машин в кластере более-менее атоматически. Выбор лежал между ini-like форматом и xml (yaml и json тоже рассматривались, но никаких существенных преимуществ, кроме уменьшения размера конфига, я не нашел), но у ini нет понятия уровней вложенности, а это мне было нужно для описания файлов статистики внутри classifier'а. Конечным решением системы, которая бы предоставляла компромисс между удобством ручного написания сложных правил и возможностью настройки параметров автоматически (через web интерфейс или же shell script), я выбрал lua + xml. То есть, логика правил описывается в lua, используя все возможности этого языка, включая, например, переменные, являющиеся функциями, а включаются эти правила, а также назначаются веса, описываются рабочие процессы в xml. Такое решение, на мой взгляд, позволяет отделить код правил от собственно процесса настройки системы. Поддержку старого формата я оставил, и теперь rspamd умеет конвертировать старый формат в xml (конвертировать правила в lua он, к сожалению, не умеет, но умеет представлять их в виде xml). Сразу же видимый профит - возможность "мягкого" рестарта с перечитыванием конфига. В будущем планируется введение динамических правил, которые можно было бы загружать в кластер через контроллер, не выполняя рестарта. Также анализ правил, заимствованных из spamassassin'а показал, что все это лучше делать через отдельные статистические файлы, которые после обучения поставлять вместе с rspamd. Ну и напоследок, если у кого-то вдруг появилось желание заменить SA или другую систему спам фильтрации на rspamd, но в rspamd не хватает какой-то функциональности, то я был бы рад выслушать подобные замечания, равно как и другие идеи по развитию проекта.

Thursday, February 4, 2010

Небольшой обзор возможностей rspamd

Так как до сих пор у меня не появилось идей, как рассказать легко и понятно о том, зачем и как использовать rspamd, я написал краткий обзор rspamd: фичи, установка, настройка и обучение. Надеюсь, он будет полезен тем, кто хочет использовать rspamd или тем, кто даже не знает о его существовании. Обзор тут:  http://cebka.pp.ru/why-rspamd.html.

Thursday, October 1, 2009

ICQ транспорт и kqueue

При настройке icq транспорта (разумеется, имеется в виду py-icqt, так как остальные существующие сейчас проекты либо мертвы, как jit, либо не работают) на FreeBSD возникло странное желание использовать kqueue reactor, так как очевидно, что традиционные select/poll - не лучший выбор при сколько-нибудь большом числе persistent коннекций. Итак, вначале необходимо поставить devel/py-kqueue. Далее, если мы пропишем в конфигурационном файле

<reactor>kqueue</reactor>

то, скорее всего pyicqt начнет кушать 100% CPU. Если посмотреть ktrace, то можно будет увидеть следующее:

25821 python2.5 RET   kevent -1 errno 9 Bad file descriptor
25821 python2.5 CALL  gettimeofday(0xbfbfd8c8,0)
25821 python2.5 RET   gettimeofday 0
25821 python2.5 CALL  gettimeofday(0xbfbfd5b8,0)
25821 python2.5 RET   gettimeofday 0
25821 python2.5 CALL  write(0x2,0x28e70014,0x2ab)
25821 python2.5 GIO   fd 2 wrote 683 bytes
"Traceback (most recent call last):
File "/usr/local/lib/jabber/pyicq/PyICQt.py", line 16, in <module>
main.main()
File "/usr/local/lib/jabber/pyicq/src/main.py", line 473, in main
reactor.run()
File "/usr/local/lib/python2.5/site-packages/twisted/internet/base.py", line 1128, in run
self.mainLoop()
--- <exception caught here> ---
File "/usr/local/lib/python2.5/site-packages/twisted/internet/base.py", line 1140, in mainLoop
self.doIteration(t)
File "/usr/local/lib/python2.5/site-packages/twisted/internet/kqreactor.py", line 189, in doKEvent
l = self._kq.kevent([], len(self._selectables), timeout)
exceptions.OSError: [Errno 9] Bad file descriptor
"
Вспоминаем неприятную особенность kqueue: при форке не наследуюся kqueue дескрипторы. То есть, необходимо выполнить демонизацию самостоятельно. Вторая проблема - неверный расчет тайм-аутов. Фикс ее:


--- kqsyscallmodule.c.bak    2009-10-02 18:24:37.000000000 +0400
+++ kqsyscallmodule.c    2009-10-02 18:26:37.000000000 +0400
@@ -141,7 +141,7 @@
}

statichere PyTypeObject KQEvent_Type = {
-  PyObject_HEAD_INIT(NULL)
+  PyObject_HEAD_INIT(&PyType_Type)
0,                             // ob_size
"KQEvent",                     // tp_name
sizeof(KQEventObject),         // tp_basicsize
@@ -295,14 +295,14 @@

/* Build timespec for timeout */
totimespec.tv_sec = timeout / 1000;
-  totimespec.tv_nsec = (timeout % 1000) * 100000;
+  totimespec.tv_nsec = (timeout % 1000) * 1000000;

// printf("timespec: sec=%d nsec=%d\n", totimespec.tv_sec, totimespec.tv_nsec);

/* Make the call */
-
+  Py_BEGIN_ALLOW_THREADS
gotNumEvents = kevent (self->fd, changelist, haveNumEvents, triggered, wantNumEvents, &totimespec);
-
+  Py_END_ALLOW_THREADS
/* Don't need the input event list anymore, so get rid of it */
free (changelist);

@@ -365,7 +365,7 @@
statichere PyTypeObject KQueue_Type = {
/* The ob_type field must be initialized in the module init function
* to be portable to Windows without using C++. */
-    PyObject_HEAD_INIT(NULL)
+    PyObject_HEAD_INIT(&PyType_Type)
0,            /*ob_size*/
"KQueue",            /*tp_name*/
sizeof(KQueueObject),    /*tp_basicsize*/


Очевидно, что для получения наносекунд, нужно умножить миллисекунды на миллион, а не на сто тысяч.

Вторая проблема - корявая демонизация. Я решил ее исправлением стартового скрипта следующим образом:

--- /usr/local/etc/rc.d/jabber-pyicq-transport.orig    2009-10-02 18:57:06.000000000 +0400
+++ /usr/local/etc/rc.d/jabber-pyicq-transport    2009-10-02 19:29:00.000000000 +0400
@@ -24,8 +24,9 @@
: ${jabber_pyicq_user="ejabberd"}

pidfile="${jabber_pyicq_piddir}/PyICQt.pid"
-command_interpreter="/usr/local/bin/python2.5"
-command="${jabber_pyicq_dir}/PyICQt.py"
-command_args="-b -o pid=${pidfile}"
+python="/usr/local/bin/python2.5"
+procname="${python}"
+command="/usr/sbin/daemon"
+command_args="${python} ${jabber_pyicq_dir}/PyICQt.py -o pid=${pidfile}"

run_rc_command "$1"

То есть, демонизация происходит при помощи /usr/sbin/daemon. Проверка трейса работы pyicqt показала, что после этих действий для работы он использует kevent, причем, использует правильно. Хотя наличие select'ов все равно смущает, но для socket IO он все же использует kqueue.

Monday, August 31, 2009

Windows resolver

Знаете, что сделает resolver винды, если его попросить отрезолвить url c base10 закодированным ip http://9715522259? Правильно, резолвер винды сможет даже 9 миллиардов превратить в ip адрес, тупо взяв младшие 32 бита от результата преобразования. Поэтому для парсинга url'ей, рассылаемых спамерами, которые содержат base10 encoded ip, надо преобразовывать его в uintmax_t, и брать младшие 32 бита.

Monday, April 13, 2009

Rspamd

Бета версия rspamd доступна для тестирования. Для сборки требуется cmake и gmime2.2. Сейчас rspamd работает примерно на порядок быстрее, чем spamassassin, но для окончательного релиза необходимо еще много тестирования. Буду признателен за любую информацию об использовании rspamd, а также о багах, в нем найденных. Rspamd доступен тут: http://cebka.pp.ru/trac