Работа с libPcap в C++

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

Библиотека Pcap (Packet Capture) позволяет создавать программы анализа сетевых данных, поступающих на сетевую карту компьютера.

Все функции библиотеки описаны в заголовочном файле pcap.h, который следует подключать к своему проекту.
Работа с pcap состит из пяти шагов:
1. Выбор устройства для анализа пакетов;
2. Открытие устройства для чтения данных;
3. Установка фильтра для анилизируемых пакетов;
4. Чтение пакетов;
5. Анализ пакетов.

Выбор устройства для анализа пакетов

Это самая простая часть работы, и сделать ее можно двумя способами: строго указать имя устройства (например, eth0 в linux, или xl1 во FreeBSD), или предоставить выбор движку pcap с помощью функции pcap_lookupdev ():

// dev = "eth0";
dev = pcap_lookupdev(errbuf);
if(dev == NULL)
{
printf("%s\n",errbuf);
return 0;
}

В качестве параметра функции pcap_lookupdev () передается строковая переменная, в которую, в случае возникновения, будет записан текст ошибки.

Открытие устройства для чтения данных

Для открытия устройства с целью последующего чтения из него данных используется функция pcap_open_live ():

pcap_t *pcap_open_live(char *device,
int snaplen,
int promisc,
int to_ms,
char *ebuf)

В качестве входных параметров ей передаются следующие данные:
1. Имя устройства, с которого будут считываться данные. Это может быть как просто строка с именем устройства, так и результат работы функции pcap_lookupdev ().
2. Число, представляющее, максимальное количество захватываемых байт. Обычно указывается BUFSIZ, что означает «максимально много».
3. Флаг, указывающий на включение так называемого «прослушивающего» (promiscuous) режима. При его включении сетевое устройство переходит режим, в котором оно принимает все пакеты в сети.
4. Количество миллисекунд, в течении которых выполнять чтение данных. Для бесконечного чтения данных здесь нужно указать -1.
5. Буфер для записи сообщений об ошибках.

В качестве выходных параметров функции выступает указатель на структуру типа pcap_t:

descr = pcap_open_live(dev,BUFSIZ,1,-1,errbuf);
if (descr == NULL)
{
printf("pcap_open_live(): %s\n",errbuf);
return 0;
}

Установка фильтра для анализируемых пакетов

Этот шаг является не обязательным, но в большинстве случаев используется разработчиками.
Благодаря фильтрам можно организовать анализ пакетов, удовлетворяющих определенному правилу и игнорируя все прочие пакеты.
Для этого нужно сделать два шага:
1. Скомпилировать фильтр с помощью функции pcap_compile ();
2. Применить скомпилированный фильтр функцией pcap_setfilter ().

Функция pcap_compile () имеет следующий синтаксис:

int pcap_compile(pcap_t *p,
struct bpf_program *fp,
char *str,
int optimize,
bpf_u_int32 netmask)

Первый аргумента функции — это структура типа pcap_t, представляющая открытую сессию для работы с устройством. Второй — ссылка на структуру, в которую будет сохранен скомпилированный фильтр. Третий — строка, представляющая собственно фильтр. Синтаксис фильтров полностью аналогичен фильтрам программы tcpdump. Четвертый параметр указывает, следует-ли «оптимзировать» фильтр, и по умолчанию равен нулю. И последний параметр — маска подсети, к которой применять фильтр.
Если при компиляции произойдет ошибка, то функция pcap_compile () вернет -1.
Применить скомпилированный фильтр к открытому устройству можно функцией

int pcap_setfilter(pcap_t *p, struct bpf_program *fp)

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

if (argv[1]){
struct bpf_program fp;
if(pcap_compile(descr,&fp,argv[1],0,netp) == -1)
{
printf("Error calling pcap_compile\n");
return 0;
}
if(pcap_setfilter(descr,&fp) == -1)
{
printf("Error setting filter\n");
return 0;
}
}

Чтение пакетов

Библиотека libPcap позволяет производить чтение пакетов в двух режимах: по одному пакету в единицу времени и в цикле определенное число пакетов.
Для превого режима применятся функция pcap_next ().

u_char *pcap_next(pcap_t *p, struct pcap_pkthdr *h)

в качестве входных параметров ей передается указатель на структуру открыой сессии и указатель на структуру, в которую будет записана базовая информация о пакете (время приема, длина). На выходе функция отдает беззнаковый char с содержимым пакета.
Для циклического приема пакетов можно заключить в бесконечный while функцию pcap_next ():

while(true)
{
packet = pcap_next(descr, hdr);
handle_packet(packet);
}

А можно не изобретать велосипед и воспользоваться функцией pcap_loop ():

int pcap_loop(pcap_t *p, int cnt, pcap_handler callback, u_char *user)

Первым входным параметром функции является указатель на структуру открытой сессии. Второй параметр — количество пакетов, которые нужно поймать. Для входа в бесконечный цикл здесь можно указать -1. Третий параметр — ссылка на callback-функцию, которую следует вызывать после получения очередного пакета. Назначение четвертого параметра — передача каких-либо дополнительных параметров в callback-функцию.
Вызов этой функции и инициализирует процедуру чтения пакетов.

Анализ пакетов

Вся процедура анализа полученных пакетов происходит в ранееописанной callback-функции, которая указывается в качесве параметра функции pcap_loop.
Формат ее таков:

void packet_handler(u_char *args,
const struct pcap_pkthdr* pkthdr,
const u_char* packet
)

В переменной args нам будет доступен дополнительный параметр, который был передан в качестве четвертого параметра функции pcap_loop. Структура pkthdr будет содержать информацию о длине пакета и времени его получения, а в переменной packet будет находиться сам пакет.
Строка packet является не совсем строкой, а набором структур, описывающих сетевой пакет, а именно: сетевой кадр, заголовок IP, заголовок TCP, заголовок UDP, и т.д. В интернете полно статей, показывающих как описать эти структуры в C++. Далее остается только устроить тайпкастинг в эти структуры и обрабатывать данные в них. Но это совсем другая история...

Скачать пример
Progg it

Поблагодарить автора

Оставить комментарий

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>