Брандмауэр. Часть 2::Журнал СА 2.2003
www.samag.ru
     
Поиск   
              
 www.samag.ru    Web  0 товаров , сумма 0 руб.
E-mail
Пароль  
 Запомнить меня
Регистрация | Забыли пароль?
Журнал "Системный администратор"
Журнал «БИТ»
Наука и технологии
Подписка
Где купить
Авторам
Рекламодателям
Архив номеров
Контакты
   

  Опросы
  Статьи

Электронный документооборот  

5 способов повысить безопасность электронной подписи

Область применения технологий электронной подписи с каждым годом расширяется. Все больше задач

 Читать далее...

Рынок труда  

Системные администраторы по-прежнему востребованы и незаменимы

Системные администраторы, практически, есть везде. Порой их не видно и не слышно,

 Читать далее...

Учебные центры  

Карьерные мечты нужно воплощать! А мы поможем

Школа Bell Integrator открывает свои двери для всех, кто хочет освоить перспективную

 Читать далее...

Гость номера  

Дмитрий Галов: «Нельзя сказать, что люди становятся доверчивее, скорее эволюционирует ландшафт киберугроз»

Использование мобильных устройств растет. А вместе с ними быстро растет количество мобильных

 Читать далее...

Прошу слова  

Твердая рука в бархатной перчатке: принципы soft skills

Лауреат Нобелевской премии, специалист по рынку труда, профессор Лондонской школы экономики Кристофер

 Читать далее...

1001 и 1 книга  
19.03.2018г.
Просмотров: 9959
Комментарии: 0
Потоковая обработка данных

 Читать далее...

19.03.2018г.
Просмотров: 8165
Комментарии: 0
Релевантный поиск с использованием Elasticsearch и Solr

 Читать далее...

19.03.2018г.
Просмотров: 8265
Комментарии: 0
Конкурентное программирование на SCALA

 Читать далее...

19.03.2018г.
Просмотров: 5233
Комментарии: 0
Машинное обучение с использованием библиотеки Н2О

 Читать далее...

12.03.2018г.
Просмотров: 5921
Комментарии: 0
Особенности киберпреступлений в России: инструменты нападения и защита информации

 Читать далее...

Друзья сайта  

 Брандмауэр. Часть 2

Архив номеров / 2003 / Выпуск №2 (3) / Брандмауэр. Часть 2

Рубрика: Администрирование /  Продукты и решения

ВЛАДИМИР МЕШКОВ

Брандмауэр

Часть 2

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

Задача программы инициализации и запуска процесса демона – принять исходные данные (правила фильтрации) и запустить на выполнение процесс-демон, передав ему эти правила. В нашем примере правилами фильтрации является IP-адрес хоста, чьи пакеты мы будем блокировать.

Процесс-демон после активизации передает модулю ядра правила фильтрации и в дальнейшем занимается ведением log-файла, в котором фиксируется время запуска/останова демона и попытки доступа с запрещенного адреса.

Теперь давайте детально рассмотрим каждую составляющую.

Программа инициализации и запуска процесса-демона

Нижеприведенный программный код разместим в файле sfc.c. Здесь будет находиться главная функция main().

Рассмотрение программы начнем с определения заголовочных файлов и переменных.

Нам понадобятся следующие header-файлы:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "sfc.h" 

В файле sfc.h определено имя файла, в котором хранится идентификатор процесса-демона (PID-файл). Файл имеет следующее содержание:

#define PID "daemon.pid"

Идентификатор процесса-демона определим как глобальную переменную:

static pid_t pid;

А теперь распишем главную функцию main().

int main (int argc, char *argv[])

{

void usage():

Это прототип функции для обработки неправильного ввода параметров. Данная функция имеет следующий вид:

void usage()

{

fprintf(stderr," Usage: daemon [ start / stop ] ");

return;

}

Программа при запуске принимает один параметр, определяющий режим ее работы:

  • start – запустить процесс-демон на выполнение;
  • stop – завершить выполнение процесса-демона.

Для работы нам понадобятся переменные:

  • int pid_file – дескриптор файла для хранения идентификатора демона;
  • struct stat s – структура для хранения атрибутов файла.

Проверяем правильность ввода входных параметров:

if(argc!=2) {

usage();

return (-1);

}

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

Режим запуска процесса-демона на выполнение

if(!(strcmp(argv[1],"start"))) {

Во избежание повторного запуска проверяем наличие в текущем каталоге PID-файла. Если файл присутствует, то демон уже запущен, о чем пользователь получает уведомление:

if(stat(PID,&s)==0) {

fprintf(stderr," Daemon is allready running ! ");

return (-1);

}

Инициализируем демон:

init_daemon();

Функция инициализации будет определена ниже.

Демон запустим как дочерний процесс.

pid = fork();

if (pid < 0) {

perror("fork");

exit(1);

}

if (pid==0) {

Отсоединяемся от терминала:

setsid();

Стартуем демон:

start_daemon();

exit(1);

}

Родительский процесс создает PID-файл и записывает в него идентификатор процесса-демона:

pid_file=open(PID,O_CREAT|O_TRUNC|O_RDWR,0644);

if(pid_file < 0) {

perror(PID);

return (-1);

}

if(write(pid_file,(char *)&pid,sizeof(pid_t)) < 0) {

perror(PID);

return (-1);

}

close(pid_file);

}

Режим остановки выполнения процесса-демона

if(!(strcmp(argv[1],"stop"))) {

Для остановки процесса-демона необходимо получить значение его идентификатора.

Это значение извлекаем из PID-файла:

pid_file=open(PID,O_RDONLY);

if(pid_file<0) {

perror(PID);

return (-1);

}

if(read(pid_file,(char *)&pid,sizeof(pid_t)) < 0) {

perror(PID);

return (-1);

}

close(pid_file);

PID-файл нам больше не нужен, удаляем его:

if(unlink(PID) < 0) {

perror(PID);

return (-1);

}

Теперь останавливаем процесс-демон, послав ему сигнал SIGINT:

kill(pid,SIGINT);

}

На этом функция main() завершается:

return (0);

}

Процесс-демон

Весь код, отвечающий за запуск, функционирование и остановку процесса-демона, разместим в файле sf_daemon.c. По сути, этот файл будет представлять собой набор функций.

Заголовочные файлы и переменные

Вначале, как всегда, определимся с заголовочными файлами и переменными. Нам понадобятся:

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <time.h>

#include <signal.h>

#include <errno.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <linux/in.h>

#include "sf_daemon.h"

Файл sf_daemon.h имеет следующее содержание:

#include <linux/types.h>

#include <linux/ip.h>

Имя log-файла:

#define LOG "/var/log/daemon"

struct data_log {

__u32 addr;

int action;

int ready;

};

В этом файле определено имя log-файла и структура data_log, в которой хранятся данные для заполнения log-файла. Назначение полей структуры следующее:

  • __u32 – IP-адрес хоста (в сетевом формате), от которого поступил пакет;
  • int action – выполняемое действие (1 – разрешить прохождение пакета, 0 – отбросить пакет);
  • int ready – флаг готовности данных в устройстве для считывания.

Поскольку наш демон работает с двумя файлами (файл устройства /dev/firewall и log-файл), то необходимо определить две переменные для хранения дескрипторов этих файлов:

int fddev=0; - дескриптор файла устройства;

int f; - дескриптор log-файла.

Функции

Первая функция, которую мы рассмотрим, останавливает выполнение процесса-демона. Вот что она из себя представляет:

void stop_daemon()

{

close(fddev);

stop_log(f);

exit(0);

}

Функция закрывает устройство, завершает ведение log-файла и осуществляет выход из программы.

Следующую функцию можно назвать центральной частью процесса-демона. Эта функция осуществляет непосредственный обмен данными с модулем ядра и заполняет log-файл. Главной особенностью данной функции является выполнение в бесконечном цикле, который прерывается только при поступлении сигнала SIGINT.

void packet_loop(void)

{

Структура для информационного обмена с модулем:

struct data_log data;

Размер блока данных, считанного из модуля:

int count;

Запускаем цикл:

for (;;) {

Считываем из модуля данные, в случае ошибки завершаем выполнение:

count=read(fddev,(char *)&data,sizeof(struct data_log));

if(count<0) stop_daemon();

Если установлен флаг готовности данных для считывания и поступил пакет с запрещенного адреса, фиксируем это событие в log-файле:

if(data.ready==1) {

if(data.action==0) {

if(fill_log(f,data.action,data.addr) < 0)

stop_daemon();

}

}

}

}

Заполнением log-файла ведает функция fill_log, к ней мы еще вернемся.

Теперь подошла очередь функции инициализации. Напомню, что ее задача – передать модулю ядра правила фильтрации (т.е. IP-адрес).

void init_daemon()

{

int err;

struct iphdr ip_pack;

В структуре ip_pack, в поле saddr (адрес источника), будет находится запрещенный IP-адрес.

Обнулим эту структуру:

memset(&ip_pack,0,sizeof(struct iphdr));

и заполним поле адреса источника:

ip_pack.saddr=inet_addr("192.168.1.10");

Подготовим к работе log-файл. Если log-файл отсутствует, создаем его:

f=open(LOG,O_CREAT|O_APPEND|O_RDWR,0644);

if(f<0) {

perror(«open log»);

exit(0);

}

Теперь передадим модулю правила фильтрации. Открываем устройство в режиме чтения/записи:

fddev=open("/dev/firewall",O_RDWR);

if(fddev<0) {

perror("firewall");

exit(0);

}

Записываем в него структуру ip_pack:

err=write(fddev,&ip_pack,sizeof(struct iphdr));

if(err<0) {

perror("firewall");

stop_daemon();

}

Итак, IP-пакеты, поступившие с хоста с адресом 192.168.1.10, будут заблокированы на входе нашей системы.

Выходим из функции:

return;

}

Если вам не понравилось, что IP-адрес введен непосредственно в исходный текст, то можете усовершенствовать код, считывая адрес из файла или из командной строки.

Теперь рассмотрим функцию, которая осуществляет запуск демона на выполнение.

void start_daemon()

{

Демон должен реагировать только на один сигнал – SIGINT. При получении этого сигнала демон завершает выполнение. Все остальные сигналы необходимо заблокировать.

Определим переменные:

sigset_t mask;

static struct sigaction act;

Создадим полный набор сигналов, исключив из него SIGINT:

sigfillset(&mask);

sigdelset(&mask,SIGINT);

Блокируем все сигналы:

sigprocmask(SIG_SETMASK,&mask,NULL);

Определяем новый обработчик для SIGINT:

act.sa_handler=stop_daemon;

sigaction(SIGINT,&act,NULL);

А теперь стартуем:

start_log(f);

packet_loop();

exit(1);

}

LOG-файл

Нам осталось рассмотреть функции для ведения log-файла. Их три:

  • start_log – запись о начале выполнения процесса-демона;
  • fill_log – запись информации о блокировании IP-пакета;
  • stop_log – запись об остановке выполнения процесса-демона.

Каждая из этих функций фиксирует текущее время возникновения того или иного события.

Все три функции разместим в файле sf_log.c.

#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

#include <fcntl.h>

#include <time.h>

#include <sys/socket.h>

#include <linux/in.h>

#define BSIZE 80

Все функции принимают в качестве аргумента дескриптор log-файла, в который будет осуществляться запись информации. В случае удачного завершения операции все функции возвращают 0, в случае ошибки -1.

Функция start_log

int start_log(int f)

{

char buf[BSIZE];

time_t start_t;

Обнулим буфер и получим текущее время:

bzero(buf,BSIZE);

time(&start_t);

Формируем буфер и записываем его в log-файл:

sprintf(buf,"Daemon started at %s", ctime(&start_t));

if(write(f,buf,strlen(buf)) < 0) return (-1);

return (0);

}

Функция stop_log

Функции start_log и stop_log практически не отличаются друг от друга, кроме имен переменных, поэтому привожу код без комментариев:

int stop_log(int f)

{

char buf[BSIZE];

time_t stop_t;

bzero(buf,BSIZE);

time(&stop_t);

sprintf(buf,»Daemon stoped at %s», ctime(&stop_t));

if(write(f,buf,strlen(buf)) < 0) return (-1);

close(f);

return (0);

}

Функция fill_log

Эта функция, кроме дескриптора log-файла, принимает IP-адрес пакета, который был заблокирован (u_long addr), и идентификатор выполненного действия (int action). Функция очень простая, и необходимости в комментариях я не вижу.

int fill_log(int f, int action, u_long addr)

{

char buf[BSIZE];

time_t fill_t;

bzero(buf,BSIZE);

time(&fill_t);

if(action==0) {

sprintf(buf,"Packet from %s was rejected at %s",inet_ntoa(addr), ctime(&fill_t));

if (write(f,buf,strlen(buf)) < 0)

return (-1);

return (0);

}

}

Makefile

Для сборки исполняемого модуля создадим Makefile следующего содержания:

CC = gcc

name = daemon

DAEMON = sfc.o sf_daemon.o sf_log.o

$(name): $(DAEMON)

$(CC) -g -o $(name) $(DAEMON)

sfc.o: sfc.c

$(CC) -c sfc.c

sf_daemon.o: sf_daemon.c

$(CC) -c sf_daemon.c

sf_log.o: sf_log.c

$(CC) -c sf_log.c

clean:

rm -f *.o

Здесь все должно быть вам знакомо. Ключ «-g» при успешной сборке можно будет заменить на «-s».

Запуск и остановка выполнения процесса-демона

После сборки в текущем каталоге появится исполняемый файл daemon. Для его запуска наберите команду:

./daemon start

Перед запуском процесса-демона необходимо загрузить модуль ядра.

После запуска демона в текущем каталоге появится файл daemon.pid. Не удаляйте этот файл! В нем хранится идентификатор процесса-демона для возможности его корректной остановки. Для остановки выполнения процесса-демона введите команду:

./daemon stop

Файл daemon.pid автоматически удаляется.

Информация о времени запуска и останова процесса-демона, а также о заблокированных пакетах будет зафиксирована в файле /var/log/daemon.

При подготовке статьи были использованы исходные тексты и документация брандмауэра SINUS (http://www.ifi.unizh.ch).


Комментарии отсутствуют

Добавить комментарий

Комментарии могут оставлять только зарегистрированные пользователи

               Copyright © Системный администратор

Яндекс.Метрика
Tel.: (499) 277-12-41
Fax: (499) 277-12-45
E-mail: sa@samag.ru