Документация по ОС FreeBSD Среда, 01.05.2024, 23:10
Приветствую Вас Гость | RSS
Меню сайта

Категории каталога
Apache [58]
DNS [25]
FTP [27]
Mail [74]
Samba [24]
Squid [46]
SSH [23]
VPN [35]
РРР [20]
Net [173]

Главная » Статьи » Сеть » Net

Сага о биллинге, или Считаем трафик на FreeBSD (ng_ipacct + perl+ MySQL) Часть 1
(Данная статья была опубликована во 2 и 3 номерах за 2005 год журнала "Системный администратор" http://samag.ru/)

Рано или поздно перед каждым системным администратором встает вопрос подсчета интернет-трафика. И тут уже не важны причины - то ли проверить провайдера, то ли проконтролировать, какой объем трафика израсходовал подключенный пользователь  и выставить счет. Конечно, систем биллинга сейчас много. И найти их в интернете не проблема. Но многие хорошие и гибкие системы учета трафика, как правило, дороги или имеют достаточно сложный интерфейс, а некоторые затрудняют использование тех же SQUID или OOPS.  (фраза прокси-серверов несколько не уместна. Ибо иногда это отражается не на них, а на ряде другого софта)

В общем, из этой ситуации мне виделось два выхода  - либо писать что-то свое, либо переделывать существующее. Исходя из соображений, что в компании уже создан корпоративный сервер статистики с единой системой авторизации и прочего, у меня не возникало особого желания  прикручивать, например, NetAms  к нему, хотя последний и не лишен ряда достоинств и преимуществ.

Да и организациям, которые получают интернет от нашей компании, не нужно то море статистики, которое выдает система биллинга. Ну, и,  конечно же, не обошлось без желания создать что-то свое, единственное и не повторимое.

В итоге, решение создать свою систему учета перевесило все остальное.
Необходимо было всего ничего, просто посчитать сколько трафика прошло через интерфейс.

Так  всё начиналось. Развилось это в большой набор скриптов, которые не только считали объем трафика, но и определяли локальный он или нет и какова его доля от общего объема. Они так же позволили просмотреть все задействованные порты и протоколы, и сколько именно трафика пришлось на каждый из них.

Что ж, скрипты это хорошо, но без самого главного, сердца всей этой системы, ничего бы и не было. Сердце ? ng_ipacct, автором которой является Роман Палагин. Эта программа, если так можно выразиться, вариация на тему ipacctd.

ipacctd работает с ipfw, а вот ng_ipacct уже с NETGRAPH плюс онаработает как модуль ядра. Почему именно ng_ipacct, а не просто ipacctd? 
NETGRAPH имеет ряд преимуществ. Наверняка, многие, замечали, как отличается объем трафика, который считаешь при помощи ipfw, и тот, который прислал провайдер со счетом.  Объясняется все достаточно просто, ipfw отрабатывает не все пакеты, поступившие в bpf - пакетный фильтр системы.  NETGRAPH выступает в данном случае как промежуточное звено, как маленькое кольцо, через которое проходят пакеты, считаются и перенаправляются дальше. Одно из его преимуществ - он работает на уровне ядра, используя минимум времени процессора и памяти. Тонкости работы и возможности его описаны  в статье "Все о NETGRAPH"  Арчи Коббса (перевод статьи на русский язык можно посмотреть на  http://www.opennet.ru/docs/RUS/netgraph_freebsd/index.html).

Мы же разберем, как установить ng_ipacct и сам NETGRAPH.

Перед тем, как делать какие либо шаги, скажу что все это протестировано на  FreeBSD 5.2.1-RELEASE-p10,  5.3-RELEASE-p4,  4.10-RELEASE-p3,  4.11-RELEASE. Стоит обратить внимание, что с  переходом на 5.3 и выше потребуется заново откомпилировать и собрать ng_ipacct. Так же это  потребуется и при каждой новой компиляции ядра(на 5-й ветке).

Таким образом, исходные данные есть. Возьмемся за netgraph. Загружать в память его можно используя  два метода: запускать нужные модули при старте либо вкомпилировать (нет имено так) сразу же в ядро. Мне предпочтителен последний вариант. 

Делается все достаточно просто. Рассмотрим на примере для FreeBSD-4.10. 

Первым делом идем в /usr/src/sys/i386/conf/  и смотрим LINT-файл:

        root@ostwest :cd /usr/src/sys/i386/conf/
        root@ostwest :less LINT  
        .............
        options         NETGRAPH                #netgraph(4) system
        options         NETGRAPH_ASYNC
        options         NETGRAPH_BPF
        options         NETGRAPH_CISCO
        options         NETGRAPH_ECHO
        options         NETGRAPH_ETHER
        options         NETGRAPH_FRAME_RELAY
        options         NETGRAPH_HOLE
        options         NETGRAPH_IFACE
        options         NETGRAPH_KSOCKET
        options         NETGRAPH_L2TP
        options         NETGRAPH_LMI
        # MPPC compression requires proprietary files (not included)
        #options        NETGRAPH_MPPC_COMPRESSION
        options         NETGRAPH_MPPC_ENCRYPTION
        options         NETGRAPH_ONE2MANY
        options         NETGRAPH_PPP
        options         NETGRAPH_PPPOE
        options         NETGRAPH_PPTPGRE
        options         NETGRAPH_RFC1490
        options         NETGRAPH_SOCKET
        options         NETGRAPH_TEE
        options         NETGRAPH_TTY
        options         NETGRAPH_UI
        options         NETGRAPH_VJC
        .............

То есть опций достаточно много и есть из чего выбрать. Для избежания проблем с разного рода устройствами можно их все включить в наше ядро, но в самом простом случае (считаем только с (нет это обозначает что ?с такого типа?  то есть предлог уместен) ethernet устройства) нам потребуются только такие опции в ядре:

        options         NETGRAPH  
        options         NETGRAPH_ETHER
        options         NETGRAPH_SOCKET
        options         NETGRAPH_TEE

Дальнейшие наши действия заключаются в компиляции ядра:

        root@ostwest :config SKIF
        Don't forget to do a ``make depend''
        Kernel build directory is ../../compile/SKIF
        root@ostwest : cd ../../compile/SKIF && make depend && make && make install && make clean && rehash

Объясним, что же тут сделано. Первая команда:

config SKIF - конфигурирование файла ядра, в моем случае это SKIF  Если ошибок в файле не было выявлено, то она выдаст такое:

        Don't forget to do a ``make depend''
        Kernel build directory is ../../compile/SKIF

Это маленькое напоминание о том, что необходимо сделать make depend и где это сделать.

cd ../../compile/SKIF && make depend && make && make install && make clean && rehash
- это полный список команд, необходимый для того, что бы перейти и скомпилировать наше ядро. Достаточно удобный, если никаких ошибок не ожидается,  но, если возникнут, то выяснить на каком этапе они произошли будет проблематично. Посему, команды лучше выполнять по отдельности.

После всех этих манипуляций перезагрузим сервер.

        root@ostwest : shutdown -r now

В принципе эту команду можно было добавить сразу же в верхнюю строку.

После перезагрузки мы получаем чистое ядро с поддержкой  NETGRAPH.

Что ж, часть работы выполнена. Устанавливаем ng_ipacct. Первым делом смотрим порты, имеющиеся в системе. Там присутствует только ipacct:

        root@ostwest :cd /usr/ports/
        root@ostwest :make search key=ipacct
        Port:   ipacctd-1.46
        Path:   /usr/ports/net-mgmt/ipacctd
        Info:   IP accounting using divert socket
        Maint:  skv@FreeBSD.org
        B-deps:
        R-deps:
        root@ostwest :

Сам же ng_ipacct можно найти здесь:

        ftp://ftp.wuppy.net.ru/pub/FreeBSD/local/kernel/ng_ipacct/ 

На сервере присутствуют версии как для четвертой ветки FreeBSD, так и для пятой. Они неидентичны, так как реализация NETGRAPH в этих версиях FreeBSD заметно отличается. Основное отличие - синхронизация. В RELENG_4 она осуществляется через уровни прерываний, о которых можно почитать в man 9 spl. Весь код netgraph должен выполняться на уровне splnet. 

Все граничные ноды, осуществляющие связь между netgraph и другой подсистемой, например ng_ether ,переходят в уровень splnet перед тем как отправить данные в граф. Если это невозможно, то данные ставятся в очередь и позже раздаются в нужной последовательности. Любые внешние вызовы, которые работают с netgraph, тоже должны первым делом вызывать splnet(). Таким образом, в одну единицу времени может существовать только один контекст выполнения netgraph и конфликтовать ему не с кем.

В RELENG_5 ядро многонитевое(multithreads) и синхронизация netgraph осуществляется с помощью мьютексов (блокировок, используемых для реализации гарантированной исключительности) и атомарных операций. Ноды передают друг другу объекты (items) различных типов: данные (mbufs), сообщения (ng_mesg), ссылки на  функции. У объекта есть атрибут - reader или writer. 

Нода может одновременно обрабатывать сколько угодно reader items или только одну writer item. По умолчанию объекты с данными - readers, а все остальные writers. Однако это можно указать как на уровне конкретных объектов, так и на уровне хуков(hooks).

Важным является то, что в момент, когда выполняется код внутри ноды, тред не держит ни одного мьютекса, что позволяет граничным нодам вызывать методы других подсистем избегая LOR(Lock order reversal ? блокирования устанавливаемых изменений). 

То есть, это грозит нам как минимум тем, что один и тот же ng_ipacct не будет работать на разных ветках FreeBSD. 

Что ж, скачиваем и распаковываем.

        root@ostwest : tar xfvz ng_ipacct-20040109.tar.gz
        root@ostwest :cd ng_ipacct/
        root@ostwest :make && make install && make clean && rehash

Ничего особо сложного здесь нет и программа без особых проблем проинсталируется. В принципе это и все, что было необходимо для установки  ng_ipacct. В комплекте к ней идут четыре скрипта, которые объясняют, как запустить программу для подсчета трафика и какостановить. Готовый скрипт для запуска и остановки: ng_ipacct_init.sh, он находиться в распакованной папке ng_ipacct/script. Этот скрипт, слегка подкорректировав, можно смело поместить /usr/local/etc/rc.d/

Все что нужно в нем прописать это:
прослушиваемые интерфейсы INTERFACES="ed0" - здесь это будет ed0. Для того, что бы указать более одного интерфейса ? перечислите их через запятую.

VERBOSE=1 - уровень расширенного вывода статистики, по умолчанию в скрипте 1, которая выведет нам дополнительно кроме IP-адреса источника и назначения количества пакетов и байт, еще и порты и протоколы, которые использовались. Стоит обратить внимание, что названия протоколов, если указан расширенный вывод(VERBOSE=1), будут отображены в числовом, а не буквенном виде. Что значит каждый номер, можно посмотреть в /etc/protocols

THRESHOLD=50000 - количество записей, которые будут храниться программой в памяти. На этот параметр стоит обратить особое внимание, так как неправильно подобранный размер threshold  может привести к потери части данных или даже к панике ядра. Это возможно по той причине, что ng_ipacct работает на уровне ядра и ей не будет доступна полностью вся память, имеющаяся на машине, а только малая часть, зарезервированная непосредственно под ядро. В результате переполнения памяти выделенной системе на ядро может произойти паника со всеми вытекающими последствиями, как-то, в лучшем случае, остановка сервера и потеря записей, относительно трафика прошедшего через него. Поэтому если у вас менее 128 Mb памяти стоит себя ограничить на уровне менее 4000-5000 записей и чаще снимать статистику, чтобы не потерять  нужные данные.

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

Передать данные в checkpoint (контрольную точку), вывести ее  при помощи show из контрольной точки и очистить контрольную точкку. 

Вот так это делается для интерфейса rl0:

        root@ostwest : ipacctctl rl0_ip_acct:rl0 checkpoint
        root@ostwest : ipacctctl rl0_ip_acct:rl0 show
        root@ostwest : ipacctctl rl0_ip_acct:rl0 clear

После show вы увидите все пакеты, которые проходили через интерфейс.
Статистика выводиться в достаточно удобном CISCO формате:

        ip_источника port_источника ip_назначения port_назначения протокол пакетов байт

Обычный режим имеет несколько другой формат вывода:

        ip_источника ip_назначения пакетов байт 

Стоит отметить, что имеется проблема с кодировками в man ipacctctl, просмотреть его удастся разве что в браузере. Ноэтолегко вылечить:

        root@ostwest : zcat /usr/share/man/man8/ipacctctl.8.gz | nroff -man | gzip > /usr/share/man/cat8/ipacctctl.8.gz 

В принципе, если вас интересует исключительно возможность поднять ng_ipacct, то на этом можно остановиться.

Мы же проследуем дальше, ибо этого мне было мало. Мне требовалось, чтобы  все данные хранились в базе MySQL для каждого хоста и интерфейса, разнесенные по дате и времени.

Вот теперь опишем основные требования, которые были предъявлены биллингу:

Первое:

Система должна хранить данные не только по-интерфейсно, но и по хостам. Объясню для чего это нужно - что бы быстро разделить трафик между разными хостами/роутерами с которых считывается статистика. При этом количество интерфейсов различно и их наименование может совпадать (почти везде есть rl0 или fxp0).

Второе:

База должна разделять трафик за текущий и предыдущий месяцы самостоятельно и иметь возможность предоставить пользователю отчет за каждый из них.  Для чего это нужно? Что бы таблицы бессмысленно не росли. Гораздо проще обработать одну маленькую за месяц, чем одну большую за год с выборкой за месяц. Просмотр статистики за предыдущие месяцы, может быть необходим дляотчета перед начальством или выставления счета клиенту, если такой имеется.

Третье:

В случае недоступности  MySQL-сервера необходимо хранить полученные данные локально до тех пор, пока не будет устранена причина недоступности сервера базы данных. После чего данные автоматически должны быть перенесены в базу при следующем сеансе.

Четвертое:

Единый конфигурационный файл с удобным и интуитивно понятным содержанием.

Пятое:

Графический или web-интерфейс, для удобоваримого отображения статистики. 

Шестое: 

Неплохо было, что бы система, где необходимо, отличала локальный трафик от внешнего.

В принципе этот список можно продолжить, но, как по мне, выше приведенные требования являются ключевыми.

Итак, требования перечислены. Создадим, исходя из этого, наш конфигурационный файл. Все свои скрипты и программы я размещаю в папки расположенные в /usr/local/script . Если у вас такой нет, рекомендую создать. Если у вас путь будет отличен от моего, тогда внесите необходимые коррективы.

Итак, создаем рабочую папку со скриптами:

        root@ostwest : mkdir -p /usr/local/script/ng_stat
        root@ostwest : chown skif:wheel /usr/local/script/ng_stat

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

        skif@ostwest : mkdir /usr/local/script/ng_stat/etc
        skif@ostwest : mkdir /usr/local/script/ng_stat/bin

Этим мы создали папки, где будут лежать наши конфигурационные и исполняемые файлы.

Что ж создадим конфигурационный файл и внесем первые параметры. По мере продвижения мы будем дополнять его нужными параметрами.

        skif@ostwest : cd /usr/local/script/ng_stat/etc

Здесь мы создадим файл настройки ng_stat.conf и внесем следующие строки.

        # Имя сервера, где находиться база данных статистики
        server_db = freebsd
        # Имя базы данных, где будет сохраняться статистика
        db_name = ng_stat
        # Имя пользователи для доступа к базе
        db_user = nguser
        # Пароль для доступа к базе
        db_pass = rfn.if
        # Имя хоста с которого снимается статистика
        listen_host = freebsd2
        # Имена интерфейсов, которые прослушиваются на компьютере.
        # Указывать через запятую
        listen_interfaces = rl0

Думаю пояснений к строкам приведенного конфигурационного файла не нужно.

Итак, первым делом откажемся от поставляемого в комплекте с ng_ipacct скрипта для  его старта и остановки. Лучшенапишемсвой

        skif@ostwest : cd /usr/local/script/ng_stat/bin
        skif@ostwest : touch ng_stat_start.pl

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

Итак первое что мы сделаем это объявим основной набор переменных:

        #!/usr/bin/perl -w
        #########################
        # Список основных переменных
        #########################
        my $serverdb = "test";
        my $dbname = "test";
        my $dbuser = "test";
        my $dbpass = "test";
        my $table_auth = "test";
        my $table_proto = "test";
        my $listen_host = "test";
        my @listen_interf;

Все переменные созвучны описанным в конфиге и являются глобальными для данного файла. Внеся заранее значение "test" в них, мы избежали проблемы получить в самом не подходящем месте undef. Но обратите внимание что, прослушиваемые интерфейсы обозначены не переменной, а массивом. Сделано это потому, что интерфейсов может быть несколько, а не один. Вот  мы и используем массив.

Почему были внесены такие непонятные значения переменных? Объясняется все достаточно просто. Во-первых, сюда можно внести значения реальных данных по умолчанию, которые будут считываться. Во вторых, если на этапе отладки будут проблемы ? изменив значения, вы сможете выяснить, с какой переменной у вас непорядок и где.

Теперь откроем конфигурационный файл и прочитаем значения наших
переменных:

        open (CONFIG, "/usr/local/script/ng_stat/etc/ng_stat.conf");
        while () {

        }
        close (CONFIG);

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

Что ж первое, что нам нужно сделать, это разобрать строки, которые поочередно считывает while до тех пор, пока не дойдет до конца файла. Но среди полезной информации конфигурационный файл несет в себе комментарии. От них нужно избавиться. Для этого в perl имеется мощнейшие инструменты поиска в строках/словах. Один из них - конструкция вида m/шаблон/ограничитель, им и воспользуемся, условившись, что комментарием будет символ # :

        $comment = '#';
        if(/^$comment/) {
        print "Коментарий\n";
        }
        else {
        # разбор строк не ограниченных коментарием
        }

Объясним конструкцию if ... else : если вначале строки присутствует символ комментария, то на экран будет выведено сообщение "Комментарий", в противном случае строка пойдет по else. Вывод сообщений о наличии комментариев нам необходим только на этапе отладки. Кстати, можете проверить, как скрипт работает,  в  последствии он будет закомментирован.

Но этого мало, необходимо разобрать и полезную строку.

        ($param,$arg) = split("=",$_);
        chomp $param;
        chomp$arg;
        $param =~ s/\s//g;
        $arg =~ s/\s//g;

Для разбора использовалась функция split, которая на основе разделителя ?=?, заданного еще в конфигурационном файле, разбила все полезные строки на две части: параметр и аргумент.Что бы избавиться от пробельных символов используется оператор замены s/шаблон/замена/ограничитель.Так как необходимо избавиться от пробельных символов, а не поменять их на что-то другое, мы  не используем параметр ?замена?, оставляя его пустым.
Модификатор \s означает любой пробельный символ. 

Перед этим были убраны из обоих переменных символы перевода строки при помощи chomp.

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

        $param =~ s/[\s\t]+//g;
        $arg =~ s/[\s\t]+//g;

Теперь необходимо присвоит каждой объявленной переменной ее истинное значение, находящееся в конфигурационном файле. В этом нам поможет конструкция следующего вида:

        if ($param eq "server_db"){
        $serverdb = $arg;
        }

Объясним. Если  левая часть полученной из файла строки соответствует server_db (смотрим наш конфигурационный файл), то  правая часть присвоится необходимой переменной.

Но у нас же есть еще несколько значений параметра в одной из строк. Их мы должны, предварительно разобрав, занести в массив. 

Листинг приведен ниже:

        #!/usr/bin/perl -w

        use DBI;
        use POSIX ":sys_wait_h";
        #########################
        # Список основных переменных
        #########################
        my $serverdb = "test";
        my $dbname = "test";
        my $dbuser = "test";
        my $dbpass = "test";
        my $table_auth = "test";
        my $table_proto = "test";
        my $listen_host = "test";
        my @listen_interf;
        my $iface_set = "no";
        my @ng_modules;
        my $ng_modules_def = "netgraph,ng_ether,ng_socket,ng_tee,ng_ipacct";
        my$threshold = 5000;
        #########################
        # Читаем конфиг. файл.
        #########################
        open (CONFIG, "/usr/local/script/ng_stat/etc/ng_stat.conf");

        while () {
            $comment = '#';
            if(/^$comment/) {
        #    print "Коментарий\n";
            }
            else {
            ($param,$arg) = split("=",$_);
            chomp $param;
            chomp $arg;
            my $razdel = "";
            $param =~ s/[\s\t]+/$razdel/g;
            $arg =~ s/[\s\t]+/$razdel/g;
            if ($param eq "server_db"){
        $serverdb = $arg;
            }
            if ($param eq "db_name"){
        $dbname = $arg;
            }
            if ($param eq "db_user") {
        $dbuser = $arg;
            }
            if ($param eq "db_pass") {
                $dbpass = $arg;
            }
            if ($param eq "table_auth") {
                $table_auth = $arg;
            }
            if ($param eq "table_protocols") {
                $table_proto = $arg;
            }
            if ($param eq "listen_host") {
                $listen_host = $arg;
            }
            if ($param eq "listen_interfaces") {
        my $coma = ',';
        if (defined $arg) {
        $iface_set = "ok";
        if ($arg ne ""){
        if ($arg =~ m/$coma/ ) {
        @listen_interf=split($coma,$arg);
        }
        else {
        @listen_interf = $arg;
        }
        }
        }
            }
            if ($param eq "ng_modules") {

        my $coma = ',';
        if ($arg =~ m/$coma/ ){
        @ng_modules = split($coma,$arg);
        } 
        else {

            @ng_modules = split ($coma,$ng_modules_def);
        }

            }

            }
        }
        close (CONFIG);

        if (!defined $listen_interf[0]) {
            print "Установите пожалуйста в режим прослушивания хотя бы один интерфейс.\n";
        }
        else {

            &check_kld_modules;
            &listening;

        }

Как видите, мы считали все параметры, и в случае, если интерфейс по какой либо причине установлен не будет, то на экран будет выдано сообщение об  этом. А если все нормально, то в массив будут внесены необходимые имена интерфейсов (например, rl0, rl1,rl2,fxp0) и, после проверки массива @listen_interf  на наличие в нем не пустых значений, будут выполнены  подпрограммы: &check_kld_modules и &listening. 

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

Вторая включает режим прослушивания интерфейсов.

Рассмотрим первую.

        subcheck_kld_modules {
            my @modules;
            my $pid;
            my $ng_module_cfg;
            my $chk_ng_file = "/tmp/ng_file";
            my $check_ng = 'kldstat -v | grep ng';
            $check_ng = "$check_ng";# " > $chk_ng_file";
            my $check_netgraph = 'kldstat -v | grep netgraph';
            $check_netgraph = "$check_netgraph";#" >> $chk_ng_file";
        #    $pid = fork;

        @modules =split ("\n", `$check_ng && $check_netgraph`);
        my $mod;
        if (defined $modules[0]) {
        foreach my $modules (@modules) {
        $modules=~ s/\d+//g;
        if ($modules =~ s/.ko//g) {
        #
        }
        else {
        $modules =~ s/[\s\t]+//g;
        $mod = "$mod $modules ";
        }
        }
        chop $mod;

        foreach my $ng_modules (@ng_modules) {
        if ($mod=~m/$ng_modules/g){
        # print "$mod содержит $ng_modules\n";
        }
        else {
        my ($pid,$kid);

        $pid = fork;
        if (defined $pid) {
        if ($pid == 0){
        print "Загрузка необходимого модуля ",$ng_modules,"\n";
        exec "/sbin/kldload $ng_modules > /dev/null 2>&1" 
or die "Ошибка загрузки модуля $ng_modules !\n";
        exit;
        }
        }
        else {
        print "Фатальная ошибка ветвления!\n.................\n";
        die "Разделение на процессы не возможно.\n Принудительный выход из дочернего процесса: $!\n";
        }
        do {
        $kid = waitpid $pid,0;
        if ($kid == -1) {
        print "Дочерних процессов в системе нет или система не поддерживает их.\n Ошибка!" and die "Выход!\n";
        } elsif ($kid == 0) {
        print "Задан не блокирующий вызов и процесс еще не завершен!\n";
        }
        } until $kid=$pid;
        undef $pid;
        }
        }
        }
        else {
        foreach my $ng_modules (@ng_modules) {
        my ($pid,$kid);

        $pid = fork;
        if (defined $pid) {
        if ($pid == 0){
        print "Загрузка необходимого модуля ",$ng_modules,"\n";
        exec "/sbin/kldload $ng_modules > /dev/null 2>&1"  or die "Ошибка загрузки модуля $ng_modules !\n";
        exit;
        }
        }
        else {
        print "Фатальная ошибка ветвления!\n.................\n";
        die "Разделение на процессы не возможно.\n Принудительный выход из дочернего процесса: $!\n";
        }
        do {
        $kid = waitpid $pid,0;
        if ($kid == -1) {
        print "Дочерних процессов в системе нет или система не поддерживает их.\n Ошибка!" and die "Выход!\n";
        } elsif ($kid == 0) {
        print "Задан не блокирующий вызов и процесс еще не завершен!\n";
        }
        } until $kid=$pid;
        undef $pid;
        }

        }
        }

Итак, первым делом объявляются действующие только в переделах этого модуля массивы и переменные. В нашем случае это @modules, куда будут заноситься все модули netgraph присутствующие в ядре или загруженные на данный момент. $check_netgraph и $check_ng переменные, в которых записаны команды, проверки загруженных модулей ядра. 

Команда эта достаточно проста и имеет вид:

        root@ostwest : kldstat -v
        ...............
        234 dummynet
        235 if_gif
        236 ipfw
        237 if_loop
        238 ng_async
        239 ng_bpf
         4    1 0xc272d000 4000     ng_ipacct.ko
                Containsmodules:
                        Name
        246 ng_ipacct
        ....................

Как вы можете заметить вывод не маленький, поэтому пришлось его урезать. Нам нужны не все  модули, а только те, которые имеют отношение к netgraph. Этим и займутся переменные, когда их используют как значения для оператораexec.

Что бы получить список загруженных модулей используется split и обратные кавычки, в качестве разделителя выступает символ переноса строки:

        @modules =split ("\n", `$check_ng && $check_netgraph`);

Дальше пойдем по проторенному пути, а именно ? выясним, имеется в массиве хоть какие то данные.  Если полученный массив не пустой, то мы выполним проверку, какие модули нам необходимо подгрузить для работы.

В данном случае информация о том из какого файла был загружен модуль(linux.ko, logo_server.ko или что-то другое) не нужна. Так же не нужны ID загруженных модулей. Для их удаления используется все тот же m//:

        $modules=~ s/\d+//g;

?\d? означает любой цифровой символ.

После удаления ID проверяется, что присутствует в выводе, информация о том из какого модуля загрузился файл или сам модуль. Однозначно на файл указывает присутствие расширения ?.ko? в строке. А потому все полученные строки, где присутствует ?.ko? подлежат удалению. В листинге вы видите, что на месте совпадения if с ".ko" стоит комментарий. Если хотите, можете провести синтаксически разбор и вывести на экран имя того  модуля, который был загружен вручную.
 
Категория: Net | Добавил: oleg (15.11.2007)
Просмотров: 1332 | Рейтинг: 0.0/0 |
Всего комментариев: 0
Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Форма входа

Beastie

Друзья сайта

Статистика

Онлайн всего: 1
Гостей: 1
Пользователей: 0
links

Copyright MyCorp © 2024