trafcnt - это набор программ для снятия и обработки статистики по трафику, проходящему через цепочки iptables. Данный пакет включает в себя следующее: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Модуль ядра. Он вставляется в любую цепочку iptables и выдает в user-space информацию обо всех проходящих через него пакетах. Для проходящих пакетов он полностью прозрачен, т.е. никак не влияет на них. Его плюсом, по сравнению с модулем ULOG, является то, что он дает примерно в 15 раз меньший overhead: 16 байт на пакет вместо 200 с лишним. Это меньше нагружает машину. Минусами данного модуля являются его непортабельность (работает только на IA32) и негибкость (в user-space отдает только то, что указано программистом, без возможности выбора администратором). Модуль состоит из следующих файлов: - ipt_TRAFCNT.h - заголовок, - ipt_TRAFCNT.c - исходник, - linuxconf.patch - патч для системы конфигурации ядра перед сборкой, - linuxmake.patch - патч для системы сборки ядра. - Модуль для пакета iptables. Он позволяет вставить модуль ядра TRAFCNT как target в любую цепочку iptables. Без него программа iptables не позволит администратору использовать -j TRAFCNT. Модуль состоит из следующих файлов: - libipt_TRAFCNT.c - исходник, - iptables.patch - патч для системы сборки пакета iptables. - Программы user-space для работы со статистикой по трафику: - trafcnd.c - демон для получения информации от модуля ядра, - trafsum.c - суммирует записи баз данных, - trafcat.c - выводит записи базы данных в текстовом виде. - Примеры скриптов для организации файлов трафика (работают из-под crond): - trafdir - складывает трафик в файлы /var/traf/год/месяц/день/час, - trafzip - архивирует каталоги /var/traf/год/месяц. - Файл README, который является единственной документацией по данному пакету. Сборка и установка в систему: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Скопировать ipt_TRAFCNT.h в /usr/src/linux/include/linux/netfilter_ipv4. - Скопировать ipt_TRAFCNT.c в /usr/src/linux/net/ipv4/netfilter. - Выполнить patch -p0 выходнаяБД Каждая запись состоит из следующих полей: +-------------------------------------------+----------------------+ Тип: | Призначные | Статистические | +----+------+------+------+--------+--------+-----+----+-----+-----+ Имя: | io | prot | y_ip | f_ip | y_port | f_port | pkt | bt | fst | lst | +----+------+------+------+--------+--------+-----+----+-----+-----+ Размер: 1 1 4 4 2 2 8 8 4 4 Описание полей: +-------+--------+-------------------------------------------------------+ | Номер | Имя | Описание | +-------+--------+-------------------------------------------------------+ | 1 | io | Направление: входящий трафик или исходящий | | 2 | prot | Протокол: TCP/UDP/ICMP | | 3 | y_ip | IP адрес какой-либо из ваших машин | | 4 | f_ip | IP адрес чужой машины | | 5 | y_port | Номер порта на вашей машине для TCP/UDP, тип для ICMP | | 6 | f_port | Номер порта на чужой машине для TCP/UDP, код для ICMP | +-------+--------+-------------------------------------------------------+ | 7 | pkt | Количество пакетов | | 8 | bt | Количество байтов | | 9 | fst | Время прихода первого пакета | | 10 | lst | Время прихода последнего пакета | +-------+--------+-------------------------------------------------------+ Технически, между prot и y_ip, есть еще 2 нулевых байта выравнивания. Но они являются несущественной деталью реализации, и поэтому в дальнейшем упоминаться не будут. Наличие 6 призначных полей в записи БД говорит о том, что программа способна различать трафик по 6 критериям. Например, если 2 машины установили между собой 1 соединение и обмениваются данными, в БД будет для них 2 записи: одна для входящего трафика, другая для исходящего (протокол, адреса и порты не меняются, но зато меняется направление). Другой пример: пользователь с помощью броузера подключился к 80 порту сервера, затем открыл еще одно окно броузера для работы с тем же сервером. В этом случае в БД будет 4 записи: входящий и исходящий трафик для одного порта броузера, входящий и исходящий трафик для другого порта (броузер для двух соединений открывает 2 порта, каждый из которых работает в 2 направлениях: 2 * 2 = 4). Если демон trafcnd обнаруживает, что появился трафик, отличающийся по любому из 6 признаков от имевшегося ранее, демон заводит для этого нового трафика новую запись в БД. Если все 6 признаков нового трафика равны уже существующей записи, новый трафик прибавляется к этой записи (новая запись не заводится). Если 6 призначных полей представляются избыточными, можно задать ключевые поля. Например: trafcnd -k 1,3 ... В этом случае демон будет различать трафик только по 2 ключевым полям: io и y_ip. В результате администратор получит сумму того, сколько каждая из его машин получила и передала данных за время сбора статистики. Для каждой машины будет только 2 записи: входящий трафик и исходящий (без подробностей о протоколах, номерах портов и адресах хостов, с которыми данная машина общалась). Ваши машины и чужие машины. ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Демон trafcnd собирает статистику только о трафике между вашими машинами и чужими. Какие машины считать вашими, указывается демону в командной строке. Например: trafcnd 192.168.0.0/24 ... В этом примере демон не будет учитывать трафик между 192.168.0.1 и 192.168.0.2 потому что обе машины - ваши. Трафик не является ни входящим, ни исходящим - он внутренний. Точно также он не учитывается между 100.101.0.1 и 200.202.0.2, потому что обе машины чужие, и трафик является внешним. Но если машина 192.168.0.1 начнет обмениваться трафиком с 100.101.0.1, демон его будет учитывать, потому что одна машина ваша, другая чужая. IP адрес вашей машины будет записан в поле y_ip, IP адрес чужой машины - в f_ip. Накопление данных. ~~~~~~~~~~~~~~~~~~ Демон trafcnd накапливает статистику по трафику в оперативной памяти. По сигналу HUP он сбрасывает данные в файл, указанный ему в командной строке. После этого он очищает свою базу данных в памяти, "забывая" о сброшенных записях. Если демону не запрещено автоматически сбрасывать данные в файл, он делает автосброс данных по достижении указанного ему лимита памяти или при недостатке памяти. Задание максимального размера памяти для БД: trafcnd -m 4000000 ... В этом примере демону указано, что под БД он может занять не более 4 миллионов байт (чуть меньше 4 Мб). Это 100 тысяч записей. Когда размер БД в памяти достигнет указанной величины, демон сбросит данные в файл, освободив этим память для новых записей. Запрет автосброса данных: trafcnd -a ... В этом примере демон не будет самостоятельно писать данные в файл. Только по сигналу HUP. Если предел по памяти достигнут, и в БД требуется вставить новую запись, демон отбросит новую запись и сообщит об этом в syslog. Выбор файла для записи: trafcnd -f /var/traf/cur ... Демон будет писать данные в указанный файл. Если файл не существует, он будет создан. Если файл существует, запись будет идти в конец файла. Если размер существующего файла не кратен размеру записи БД (40 байт), демон начнет запись с кратного смещения, предполагая, что при предыдущей записи произошел сбой, и последняя (частичная) запись БД недействительна. Каталог для указанного файла должен существовать в любом случае - демон его не создает. Путь к файлу должен быть указан как абсолютный (а не относительный), потому что после запуска демон меняет свой текущий каталог на корневой. Если имя файла не указано, программа не уходит в фон, и пишет данные на stdout. При этом, если stdout является терминальным устройством, программа выводит сообщение об ошибке и завершает работу. Syslog. ~~~~~~~ Сообщения об ошибках демон записывает в системный журнал. Используется facility daemon и level err. Сообщения, способные забить весь syslog, демон отправляет только один раз за всю жизнь своего процесса. К сообщениям такого типа относятся недостаток памяти, неправильные пакеты от модуля ядра, ошибки чтения сокета и подобные, имеющие способность повторяться с очень высокой частотой. Сообщения о прочих проблемах (возникающих нерегулярно или с малой частотой), пишутся в syslog всегда - столько раз, сколько возникает ошибочная ситуация. Если программа не ушла в фон, она пишет сообщения об ошибках на stderr. Идентификация. ~~~~~~~~~~~~~~ Демон trafcnd, как и любой другой приличный демон, записывает свой PID в файл, находящийся в каталоге /var/run. Поскольку в системе может быть несколько процессов trafcnd с разными группами netlink, демон поступает аналогично pppd - к имени файла добавляет номер группы netlink. Таким образом, имя файла выглядит как /var/run/trafcnd${nlgroup}.pid где nlgroup - номер группы. Примеры: /var/run/trafcnd1.pid /var/run/trafcnd5.pid /var/run/trafcnd32.pid При корректном завершении работы (по сигналу TERM), демон сбрасывает БД из памяти в файл данных и удаляет файл со своим PID. При запуске демон пытается создать файл для записи PID. Если такой файл уже существует, программа сообщит об ошибке и завершится. Два демона на одну группу netlink вешать бессмысленно. Если же ранее запущенный демон уже умер, его файл с PID должен быть удален вручную. Файл с PID демона может остаться после смерти демона только при некорректном его завершении (сигналом KILL). Командная строка trafcnd. ~~~~~~~~~~~~~~~~~~~~~~~~~ Полный список опций командной строки демона может быть получен с помощью команды trafcnd -h Демон выведет на stdout подробную справку и завершится. Пример запуска демона: trafcnd 192.168.0.0/24 192.168.1.1-192.168.1.5 -f /var/lib/traffic Демон может жить в любом каталоге системы и не зависит от каких-либо файлов. Демон не умрет при отстутствии каталога /var/run и того каталога, в который он должен писать файл данных. Если демон запустился, то сам по себе он уже не умрет, независимо от нехватки памяти, ошибок записи данных и вообще каких-либо ошибок. Запуск демона с опцией -d (среди прочих) приведет к тому, что он выведет на stdout все заданные ему настройки и завершится. Эта опция может использоваться для того, чтобы убедиться: программа будет делать именно то, что ей указано. Организация данных. ~~~~~~~~~~~~~~~~~~~ Демон trafcnd пишет данные в один файл. Что будет делаться с этим файлом дальше - демону безразлично. Если демон работает достаточно долго, файл может стать довольно большим. Чем больше будет файл, тем сложнее будет выбрать из него нужную информацию. Один из способов организации данных: периодически посылать демону сигнал HUP и переименовывать созданный им файл в соответствии с некоторой схемой, включающей в себя указание текущего времени в имени файла. Например, можно раз в час выполнять следующие команды: killall -HUP trafcnd sleep 5 mv /var/traf/cur /var/traf/`date '+%m-%d-%H.db'` В результате каждый час трафик будет сбрасываться в файлы с именами вида ММ-ДД-чч.db где ММ - номер месяца, ДД - день месяца, чч - час. Таким образом, будет 24 файла в сутки. Каждый файл будет содержать информацию только об одном часе. В приведенном выше примере предполагается, что демон пишет данные в файл /var/traf/cur, и почасовые файлы должны быть в каталоге /var/traf. Пауза в 5 секунд присутствует для гарантии того, что демон закончил запись. Теоретически, демон сбрасывает данные мгновенно, одним системным вызовом, независимо от объема данных. Однако, на практике, сервер может быть сильно нагружен, файл может быть на NFS, может потребоваться сброс кэша файловой системы на диск и т.п. Величина задержки зависит главным образом от быстродействия накопителя (если его кэширование не выполняется). Сброс большого объема данных на дискету, смонтированную в синхронном режиме, может выполняться достаточно долго. В случае вышеописанного подхода к организации данных, наиболее оптимальным решением будет повесить нужные команды на crond. Для примера можно взять скрипты trafdir и trafzip. Скрипты написаны в предположении, что они запускаются из-под моей версии crond. Мой демон перед запуском команды устанавливает переменные окружения в значения, соответствующие тому времени, когда команда должна быть запущена. Эти переменные используются в скриптах. При запуске из-под другой версии crond текущее время может быть получено командой date. Пример конфига crond: # Г М ДМ ДН ч м с команда #---------------------------------------- * * * * * 59 45 kill -HUP `cat /var/run/trafcnd1.pid` * * * * * 59 55 trafdir * * 1 * 0 0 0 trafzip Обозначения полей: - Г - год, - М - месяц, - ДМ - день месяца, - ДН - день недели, - ч - час, - м - минута, - с - секунда. В конце каждого часа дается команда демону trafcnd на сброс БД в файл. Через 10 секунд после этого запускается скрипт trafdir. Он создает необходимые каталоги (если их нет), затем перемещает файл БД в нужный каталог, попутно переименовывая его и сортируя (на всякий случай). Таким образом, на каждые сутки получается 1 каталог, в котором лежат 24 файла. Записи каждого файла содержат информацию только по тому часу, который равен имени файла. Раз в месяц (1 числа, в 00:00:00) запускается скрипт trafzip, который архивирует каталог 2-месячной давности в tar.bz2. Затем удаляет каталог. Как организована информация: /var /traf /03 #год /11.tar.bz2 #архив за прошлый месяц /12 #текущий месяц /01 #день месяца /01 #почасовые файлы БД /02 /03 ... /23 Имя каталога данных за 28 декабря 2003 года (без префикса /var/traf) выглядит так: 03/12/28. Имя файла данных за период с 6 до 7 часов этого дня: 03/12/28/06. Вывод данных. ~~~~~~~~~~~~~ Информация в файле БД хранится в двоичном виде. Для преобразования ее в текст предназначена программа trafcat. Она читает записи из указанных ей входных файлов (по умолчанию stdin), преобразует их в текстовые строки и выводит в выходной файл (по умолчанию stdout). Одна запись БД преобразуется в одну строку текста. Поля записи отделяются друг от друга указанным разделителем (по умолчанию табуляцией). Строка завершается кодом конца строки (\n). Записи БД выглядят примерно так: I 6 192.168.0.2 200.30.4.5 4321 80 2 158 03.12.30,10:05:48 03.12.30,10:06:01 В этом примере в качестве разделителя полей использовался пробел, чтобы вся запись поместилась в одну строку экрана (80 символов). Порядок вывода полей записи соответствует порядку в котором они располагаются в БД. Чтобы долго не листать, вот еще раз список полей: +-------+--------+-------------------------------------------------------+ | Номер | Имя | Описание | +-------+--------+-------------------------------------------------------+ | 1 | io | Направление: входящий трафик или исходящий | | 2 | prot | Протокол: TCP/UDP/ICMP | | 3 | y_ip | IP адрес какой-либо из ваших машин | | 4 | f_ip | IP адрес чужой машины | | 5 | y_port | Номер порта на вашей машине для TCP/UDP, тип для ICMP | | 6 | f_port | Номер порта на чужой машине для TCP/UDP, код для ICMP | +-------+--------+-------------------------------------------------------+ | 7 | pkt | Количество пакетов | | 8 | bt | Количество байтов | | 9 | fst | Время прихода первого пакета | | 10 | lst | Время прихода последнего пакета | +-------+--------+-------------------------------------------------------+ Вышеприведенный пример говорит о том, что ваша машина в 10:05:48 30 декабря 2003 года связалась с чужим web-сервером и получила от него 2 пакета суммарным размером 158 байт. Сеанс связи закончился в 10:06:01 того же числа. IP адрес вашей машины 192.168.0.2, чужой машины - 200.30.4.5. Ваша машина для связи использовала порт 4321. Почему именно получила? Потому что направление передачи = I (incoming). Почему от web-сервера? Потому что порт чужой машины = 80, а протокол = 6 (TCP). Откуда известно, что web-сервер чужой, а не ваш? Потому что, если бы он был ваш, то трафик между вашим клиентом и вашим же сервером не попал бы в базу данных. Несколько смущать может дата. Она записывается в формате ГГ.ММ.ДД,чч:мм:сс то есть по убыванию - от года к секундам. Это сделано для того, чтобы вывод программы trafcat можно было легко обработать скриптом. Например, для сортировки по времени можно сравнивать поля времени именно как текстовые строки, без преобразования их в time_t. В случае принятого в России формата ДД.ММ.ГГ, при сравнении строк дат, 30.12.03 будет больше чем 01.01.04, что, разумеется, неверно. Программа trafcat умеет преобразовывать IP адреса и номера протоколов и портов в имена хостов, протоколов и сервисов. Требуемые преобразования включаются опциями командной строки. По умолчанию преобразования не выполняются, выдаются числа (в текстовом виде, разумеется, а не в двоичном). Если требуемое преобразование не может быть выполнено, соответствующее поле выводится в числовом виде. Например, это может произойти с IP адресами, для которых resolver не может получить символьное имя - они так и останутся числовыми адресами. Подробную справку по опциям программы можно получить с помощью опции -h: trafcat -h Программа выведет на stdout список опций, значения по умолчанию и завершится. Программа trafcat работает как фильтр - она не изменяет входные файлы. Программа может жить в любом каталоге файловой системы. Программа не зависит от каких-либо файлов или каталогов в системе. Однако, если требуются преобразования, понадобятся файлы /etc/services, /etc/hosts, /etc/resolv.conf и т.п (без них преобразования работать не будут, будут выводиться номера портов и IP адреса). Суммирование данных. ~~~~~~~~~~~~~~~~~~~~ Для суммирования записей одного или нескольких файлов БД предназначена программа trafsum. По умолчанию она суммирует статистические поля всех записей с одинаковыми призначными полями. Если требуется получить менее подробную (и более сжатую) статистику, программе можно задать список ключевых полей. Программа работает как фильтр: читает указанные входные файлы (по умолчанию stdin), выполняет суммирование записей и пишет полученные записи в выходной файл (по умолчанию stdout). Для чего вообще нужна программа trafsum, если демону trafcnd можно задать список ключевых полей и свести таким образом воедино интересующую информацию? Во-первых, оперативная память не резиновая, и демон рано или поздно должен будет сбросить данные на диск. Внутри одного сброшенного им блока не может быть записей с одинаковыми ключевыми полями, но в разных блоках они вполне могут быть, т.к. демон "забывает" о сброшенных записях. Во-вторых, если какие-то поля не указаны демону как ключевые, то впоследствии невозможно будет различить записи по этим полям. Например, если номера портов не указаны как ключевые, то демон будет писать в эти поля нули. Почему? Потому что в одно поле невозможно записать порты 21, 22, 80, 1234, 5678 и т.д. Только какой-то один. Поскольку поле не является ключевым, пакеты не различаются по этому полю, и в одну запись БД пойдут все пакеты, безотносительно портов, если они не отличаются по другим признакам. Демон не знает, что писать в это поле. Порт первого пакета? Порт последнего пакета? Он пишет туда нуль. Поэтому представляет интерес такой вариант: загрести демоном как можно более развернутую статистику. А потом можно будет поиметь как подробную информацию, так и более общую. С помощью программы trafsum. Программа trafsum может свести воедино достаточно большое количество записей, но все же не бесконечное. В случае сортированных входных файлов, программа держит в памяти по одной записи (40 байт) из каждого файла. Ограничение здесь только по количеству открытых файлов на процесс (по памяти тоже, но меньше). В случае несортированных входных файлов, программа считывает в память весь такой файл и сортирует его записи. Здесь наиболее критичным является ограничение по памяти на процесс (или по общему объему имеющейся виртуальной памяти). Демон trafcnd сбрасывает данные в сортированном виде, но если файл содержит более одного сброшенного блока, значит он не сортирован. Демон сортирует записи не для красоты, а для скорости работы с ними. Основной задачей демона является найти существующую запись и добавить новые данные к ней. Поиск в сортированном массиве выполняется с гораздо более высокой скоростью, чем в несортированном. По скорости получается выигрыш, даже с учетом накладных расходов на перемещение записей в памяти. Почему для программы trafsum важно, сортированы ее входные файлы или нет? Для нее основной задачей является найти все одинаковые записи и свести их в одну. В случае несортированных файлов, алгоритм выглядит так: взять первую запись из первого файла, сравнить ее со всеми записями этого файла, затем со всеми записями второго файла, третьего ..., вывести запись в выходной файл. Взять вторую запись из первого файла, повторить те же действия. Взять третью запись, четвертую... Повторить для второго файла, для третьего ... Если находится совпадение, помечать в памяти данную запись как уже использованную. При таком подходе программа создаст огромную нагрузку на сервер и при этом будет работать недопустимо долго. В случае сортированных файлов, алгоритм выглядит так: считать первые записи из всех файлов. Найти из них наименьшую. Сбросить ее на выход. Считать вместо нее следующую запись из этого файла. Повторять пока количество записей хотя бы в одном файле больше нуля. Если находится совпадение, засуммировать 2 записи и считать вместо одной из них следующую из одного файла. Такой алгоритм работает гораздо быстрее и при этом меньше нагружает файловую подсистему ядра. Программа trafsum может одновременно обрабатывать как сортированные файлы, так и несортированные. Если файл не сортирован, он считывается в память и сортируется. Потом программа работает с массивом в памяти как с сортированным файлом (только без переключений контекста в ядро и обратно). Сама программа не знает, сортирован данный файл или нет. Это зависит от организации данных, которая отдается на усмотрение администратора. Поэтому при запуске программы ей следует указать, сортирован данный файл или нет. Если не указывать, программа считает что не сортирован и использует для этого файла более ресурсоемкий алгоритм полного-чтения-и-сортировки-в-памяти. Если программе указано, что данный файл сортирован, а при его обработке оказывается что нет, программа выводит сообщение об ошибке и завершает работу. Она не знает, что ей делать дальше. По идее, надо удалить выходной файл и начать все сначала, отсортировав данный входной файл в памяти. Но выходной файл может уже обрабатываться другой программой, да и вообще такое самоуправство - дурной стиль. Поэтому программа сообщает об ошибке и завершает работу. Администратор может снова запустить программу, указав ей данный файл как несортированный. Если программе указан список ключевых полей, в котором отсутствует хотя бы одно призначное поле, программа считает все свои входные файлы несортированными, игнорируя опцию -s. Входные файлы задаются с помощью опций -s и -u. Первая означает, что следующий за ней аргумент является именем сортированного файла, вторая - именем несортированного файла. Для файлов, заданных в командной строке без явного указания -s или -u, используется последняя опция -s или -u. Например: trafsum -s *.db -u cur означает, что первый файл .db сортирован, а все прочие - нет. Потому что после расширения оболочкой символа * командная строка будет выглядеть так: trafsum -s 1.db 2.db 3.db -u cur Программа считывает свои аргументы с помощью getopt(3). Получается: trafsum -s 1.db -u cur 2.db 3.db В данном случае лучше запустить программу так: trafsum -u cur -s *.db Описанное поведение можно считать багом. Получив опцию -s или -u, программа должна извлечь все аргументы, не являющиеся опциями, потом снова вызвать getopt. Возможно, когда-нибудь в будущем так и будет сделано. Для получения справки по опциям, можно запустить программу так: trafsum -h Программа выведет на stdout список опций со значениями по умолчанию и завершит работу. Как суммируются статистические поля двух записей: - количество пакетов складывается, - количество байтов складывается, - время получения первого пакета устанавливается наименьшим из двух, - время получения последнего пакета устанавливается наибольшим из двух. Программа trafsum не зависит от каких-либо файлов и каталогов системы. Программа может жить в любом каталоге. Примеры извлечения информации. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Получение суммарной информации о трафике за декабрь месяц: traf# trafsum -k 1,3 /var/traf/03/12/*/* | trafcat | cut -f 1,3,8 I 192.168.0.1 15762874 I 192.168.0.2 23980076 O 192.168.0.1 317652 O 192.168.0.2 1140028 Сначала выведен входящий трафик в байтах для всех машин локалки, затем исходящий. В примере приведено 2 машины. Можно посмотреть подробную статистику за месяц по интересующему хосту: trafsum -s /var/traf/03/12/*/* | trafcat | grep 192.168.0.2 Можно определить, с каких IP адресов заходили вчера по ssh на ваш шлюз: trafsum -k 1,2,3,4,5 /var/traf/03/12/28/* | trafcat -s | grep ssh Аналогично, кто из чужих ломился на порты 135-139 сегодня между 8 и 9 часами: trafsum -k 2,3,4,5 /var/traf/03/12/29/08 | trafcat -p |\ cut -f 2,3,4,5 | grep -v icmp | grep '13[5-9]$' Технические подробности. ~~~~~~~~~~~~~~~~~~~~~~~~ Модуль ядра ipt_TRAFCNT для каждой группы netlink поддерживает 4096-байтный буфер и 1-секундный таймер. Буфер служит для накопления информации о пакетах IPv4, проходящих по цепочкам iptables через модуль ipt_TRAFCNT. Память под буфер выделяется при получении первого пакета для данной группы netlink. Освобождает память из-под буфера сетевой код ядра после копирования в user-space. Таймер предназначен для отправки буфера в user-space по истечении 1 секунды. Таймер запускается после получения первого пакета для данной группы netlink и останавливается перед отправкой буфера в user-space. Первые 16 байт буфера содержат заголовок пакета netlink. Заголовок имеет тип struct nlmsghdr. После заголовка идут пакеты модуля ipt_TRAFCNT. Каждый пакет занимает 16 байт и имеет тип trafcnt_packet_msg_t. Минимальное количество пакетов trafcnt_packet_msg_t, отправляемых в user-space, равно 1. Максимальное количество равно 4096 / 16 - 1 = 255. Точный размер пакета netlink в байтах указан в заголовке netlink. Структура пакета netlink, отправляемого в user-space: +-------------------------+ | struct nlmsghdr | +-------------------------+ | trafcnt_packet_msg_t #1 | | trafcnt_packet_msg_t #2 | | ... | | trafcnt_packet_msg_t #N | +-------------------------+ Описание полей struct nlmsghdr содержится в заголовке , описание trafcnt_packet_msg_t - в . Пакет отправляется в user-space по заполнении буфера или по истечении 1 секунды от начала записи в буфер - в зависимости от того, какое событие произойдет первым. Пакет отправляется всем процессам, сделавшим bind(2) на AF_NETLINK, nl_groups == данная группа netlink. Демон trafcnd, получив пакет от модуля ядра, преобразует каждый 16-байтовый trafcnt_packet_msg_t в 40-байтовую запись БД. Если запись с такими же призначными полями уже есть, новая запись прибавляется к существующей. В противном случае для новой записи выделяется место в БД. Структура записи БД описана в файле trafcnd.c. Она называется record. Перед поиском существующей записи, на новую запись накладывается маска. В этой маске биты ключевых полей равны 1, а всех прочих - 0. Маска называется key и имеет тип record. Новая запись объединяется с маской побитовым AND. Затем выполняется поиск точно такой же записи или места для вставки. Используется алгоритм двоичного поиска в сортированном массиве (каковым является БД в ОЗУ). Если для новой записи нет места в БД, разрешен автосброс данных в файл, и предыдущий сброс данных выполнен без ошибок (или данная попытка - первая), то демон пытается сбросить данные в файл и освободить место для новой записи. Если сброшена хотя бы одна существующая запись, демон вставляет новую запись в БД. Если ни одной - новая запись отбрасывается. Если данный случай выбрасывания записи - первый, демон сообщает об этом в syslog. Получив сигнал HUP, демон пытается записать БД в файл, если количество записей в БД отлично от нуля. При успешном завершении этой операции демон сбрасывает признак ошибки (если он был установлен), чтобы разрешить последующие операции автосброса (если они не запрещены опцией командной строки). При ошибке записи устанавливается флаг ошибки, и последующий автосброс данных не выполняется, даже если он разрешен - до следующей успешной записи по сигналу HUP. Отсутствие записей в БД ошибкой не является. Логика сброса данных в файл: - Сигнал HUP: - если количество записей в БД != 0: - попытка записи БД в файл; - если успешно: - флаг ошибки = 0; - иначе: - флаг ошибки = 1; - Отсутствие памяти для новой записи: - если автосброс данных разрешен: - если флаг ошибки == 0: - попытка записи БД в файл; - если успешно: - флаг ошибки = 0; - иначе: - флаг ошибки = 1; - если нет места для новой записи: - если флаг сообщения == 0: - сообщение в syslog; - флаг сообщения = 1; Получив сигнал TERM, демон пишет БД в файл данных, удаляет файл со своим PID и завершает работу. Смерть демона может быть вызвана только сигналом ему извне. Никакие внутренние причины к смерти демона не приводят. При любых ошибках демон будет пытаться продолжать работу - даже в том случае, если это представляется бессмысленным. Причина такого поведения в следующем: если программа успешно запустилась, она не имеет права завершиться до тех пор, пока либо не выполнит работу до конца, либо выполнение этой работы станет невозможным. Окончание работы демона определяет администратор с помощью сигнала TERM. А проблемы могут быть лишь с файловой системой и отсутствием памяти. И те и другие могут быть решены администратором: созданием отсутствующего каталога, очисткой диска от ненужных файлов, прибиванием лишних процессов и т.п. Если проблема была с ФС, демону достаточно послать сигнал HUP, и он продолжит работу. При этом накопленная им в памяти информация не будет потеряна.