Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Сегодня многие крупные компании для увеличения производительности открывают доступ сотрудников с рабочих компьютеров в Интернет. Однако не все сотрудники используют его продуктивно и безопасно для рабочих сетей, по этой причине требуется контроль доступа. Из нежелательных явлений работы в Интернете можно назвать:
● посещение нежелательных сайтов;
● скачивание файлов, потенциально зараженных;
● просмотр видеозаписей и скачивание изображений;
● общение в соц. сетях, онлайн-игры.
Для обеспечения эффективной работы сети необходимо управлять потоком трафика, например, с помощью интернет-запросов.
Packet Capture позволяет создавать программы, анализирующие данные, поступающие на сетевую карту компьютера, и (в более новых версиях), передающие пакеты в сеть. В различных подходах к мониторингу и тестированию сети, так называемых программах-снифферах (англ. sniff - нюхать), используют эту библиотеку. Она предназначена для использования совместно с языками C/C++, а для работы с библиотекой на других языках используют обертки. Для Unix-подобных систем - библиотека libpcap, а для Microsoft Windows - WinPcap.
В качестве примера, создадим простейшее консольное приложение, демонстрирующее работу библиотеки pcap под ОС Microsoft Windows, которое будет состоять из двух частей: отправителя, формирующего каждый байт сообщения, и получателя.
Отправитель
1. Определим IP-адрес отправителя. В действительности компьютер может входить в несколько подсетей одновременно и для каждой из них иметь отдельный IP-адрес. Для простоты кода ограничимся единственным IP.
u_char* learn_IP()
{
addrinfo hnts, *pRes;
char hostName[1024];
u_char* ip_addr = NULL;
if (gethostname(hostName, sizeof hostName) == 0)
{
memset(&hnts, 0, sizeof hnts);
hnts.ai_family = AF_INET; hnts.ai_socktype = SOCK_DGRAM; hnts.ai_flags = AI_PASSIVE;
if (getaddrinfo(hostName, NULL, &hnts, &pRes) == 0)
{
struct addrinfo* res;
char buffer[INET_ADDRSTRLEN];
for (res = pRes; (res != NULL) && (res->ai_family != AF_INET); res = res->ai_next);
ip_addr = (u_char* )inet_ntop(AF_INET, &((struct sockaddr_in *)res->ai_addr)->sin_addr, buffer, INET_ADDRSTRLEN);
freeaddrinfo(pResults);
}
WSACleanup();
}
return ip_addr;
}
Далее специальная функция определяет MAC-адрес сетевой карты компьютера, отправляющего сообщение.
u_char* learn_MAC()
{
IP_ADAPTER_INFO ip_ainf[128];
PIP_ADAPTER_INFO pip_ainf = ip_ainf;
u_long bufLen = sizeof(ip_ainf);
GetAdaptersInfo(ip_ainf, &bufLen);
u_char* mac_addr = pip_ainf->Address;
return mac_addr;
}
2. В Интернет-заголовке необходимо указать MAC-адреса отправителя и получателя, которые определяются посредством ARP-запроса по известному IP-адресу. Если отправитель и получатель находятся в разных локальных сетях, то ARP-запросом требуется определить MAC-адрес маршрутизатора сети отправителя и его же указывать в сообщении. Если отправитель и получатель в одной сети, то конечного адресата. В любом случае ARP-пакет шлется в широковещательном режиме.
IPAddr DestIp = 0, SrcIp = 0;
static u_long MacAddr[2];
u_long PhysAddrLen;
SrcIpAddr = inet_addr(SrcIpString); // IP-адрес отправителя
DestIpAddr = inet_addr(destIpString);// IP-адрес маршрутизатора или конечного получателя
memset(&MacAddr, 0xff, sizeof(MacAddr)); // Указатель на широковещательную рассылку
if (SendARP(DestIpAddr, SrcIpAddr, &MacAddr, &PhysAddrLen) == NO_ERROR)
*bPhysAddr = (BYTE *) & MacAddr; // Искомый MAC-адрес
3. Далее с помощью стандартных функций библиотеки pcap определяем интерфейс, который будет использоваться для передачи сообщений.
pcap_if_t *alldevs, *dev;
char errbuf[PCAP_ERRBUF_SIZE];
int inum, i = 0;
pcap_findalldevs(&alldevs, errbuf);
scanf_s("%dev", &inum);
for (dev = alldevs, i = 0; i < inum - 1; dev = dev->next, i++);
Открываем его.
pcap_t *adhandle;
adhandle = pcap_open_live(dev->name, 65536, 0, 1000, errbuf);
4. Далее собираем воедино пакет для отправки. Определяем его Интернет, IP и UDP-заголовки, канального, сетевого и транспортного уровней. Особого внимания заслуживает функция вычисления контрольной суммы для IP и UDP-заголовков.
u_short checksum(u_char *buffer, int size)
{
u_long chksum = 0;
while (size > 1)
{
chksum += *buffer++;
size -= sizeof(u_short);
}
if (size)
chksum += *(u_char*)buffer;
chksum = (chksum >> 16) + (chksum & 0xffff);
chksum += (chksum >> 16);
return (u_short)(~chksum);
}
Затем пакет отправляется посредством стандартной функции библиотеки pcap.
pcap_sendpacket(adhandle, packet, 43);
adhandle – ранее определенный указатель на интерфейс отправки пакета, packet – сам пакет, последний параметр – размер пакета в байтах.
Получатель
1. С помощью стандартных функций pcap определяем интерфейс, который будет использоваться для перехвата сетевого трафика и открываем его (по аналогии с отправкой пакетов). Далее специальной функцией библиотеки pcap настраиваем и устанавливаем фильтр пакетов.
char pckfilter[] = "ip dst host 192.168.1.1";
struct bpf_program fcode;
if (d->addresses != NULL)
netmask = ((struct sockaddr_in *)(d->addresses->netmask))-> sin_addr.S_un.S_addr;
else netmask = 0xffffff;
pcap_compile(adhandle, &fcode, pckfilter, 1, netmask);
pcap_setfilter(adhandle, &fcode);
2.Далее запускается функция, которая в бесконечном цикле обрабатывает перехваченные пакеты.
void packet_handler(u_char *param, const struct pcap_pkthdr *pkt_header, const u_char *pkt_data)
а) Для начала из заголовка функции выуживается время передачи пакета и преобразуется в удобный для чтения формат.
time_t local_tv_sec = header->ts.tv_sec;
char strtime[16];
struct tm ltime;
localtime_s(&local_tv_sec, <ime);
strftime(strtime, sizeof strtime, "%H:%M:%S", <ime);
б) Из всего заголовка выделяется та часть, которая соответствует IP-протоколу сетевого уровня. Проверяется контрольная сумма, и в случае ее совпадения сообщение обрабатывается вверх по стеку TCP/IP, иначе – сообщение игнорируется.
ip_header *ip_hd;
ip_hd = (ip_header *)(pkt_data + 14);
if (!Count_ip_check_sum(ip_hd))
return;
в) Определяем UDP-заголовок принятого пакета и проверяем его контрольную сумму. Для этого формируем псевдозаголовок, состоящий из IP-адресов отправителя и получателя, поля, определяющего протокол нижележащего уровня, и длины UDP-заголовка и данных. Контрольная сумма вычисляется по UDP-заголовку плюс псевдозаголовок. От нее зависит дальнейшая судьба пакета: принять или отбросить.
u_int ip_len = (ip_hd->ver_ihl & 0xf) * 4;
udp_header *udp_hd;
udp_hd = (udp_header *)((u_char*)ip_hd + ip_len);
u_char* pshd = new u_char[12]; // псевдозаголовок
pshd[0] = ip_hd->src_ip.byte1; pshd[1] = ip_hd->src_ip.byte2;
pshd[2] = ip_hd->src_ip.byte3; pshd[3] = ip_hd->src_ip.byte4;
pshd[4] = ip_hd->dest_ip.byte1; pshd[5] = ip_hd->dest_ip.byte2;
pshd[6] = ip_hd->dest_ip.byte3; pshd[7] = ip_hd->dest_ip.byte4;
pshd[8] = 0x00; pshd[9] = 0x11; pshd[10] = 0x00; pshd[11] = 0x09;
if (!Count_udp_check_sum(udp_hd, pshd))
return;
г) Далее определяем значение портов в UDP-заголовке и выводим на экран сводную информацию о сообщении: время отправки, длина, IP-адреса отправителя и получателя и их порты.
u_short sendport = ntohs(udp_hd->src_port);
u_short destport = ntohs(udp_hd->dest_port);
printf("%s,%.6d len:%d\n", timestr, header->ts.tv_usec, header->len);
printf("%d.%d.%d.%d.%d -> %d.%d.%d.%d.%d\n",
ip_hd->src_ip.byte1, ip_hd->src_ip.byte2, ip_hd->src_ip.byte3, ip_hd->src_ip.byte4, sendport,
ip_hd->dest_ip.byte1, ip_hd->dest_ip.byte2, ip_hd->dest_ip.byte3, ip_hd->dest_ip.byte4, destport);
В результате разработано приложение, наглядно демонстрирующее применение библиотеки pcap в задачах контроля и анализа сетевого трафика. Оптимизация использования сетевых ресурсов является обязанностью системного администратора, и данная библиотека является хорошим помощником в этом.