Чтение с разнородного сетевого устройства
Я хочу написать инструмент для анализа беспроводного трафика в режиме реального времени.
Кто-нибудь знает, как читать с разнородного (или нюхающего) устройства в C?
Я знаю, что для этого вам нужен root-доступ. Мне было интересно, если кто-нибудь знает, какие функции необходимы для этого. Обычные розетки здесь не имеют смысла.
5 ответов
В Linux вы используете сокет PF_PACKET для чтения данных с необработанного устройства, такого как интерфейс Ethernet, работающий в случайном режиме:
s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))
Это отправит копии каждого полученного пакета на ваш сокет. Вполне вероятно, что вам не нужен каждый пакет. Ядро может выполнять фильтрацию первого уровня, используя BPF, пакетный фильтр Berkeley. BPF - это виртуальная машина на основе стека: она обрабатывает небольшой набор инструкций, таких как:
ldh = load halfword (from packet)
jeq = jump if equal
ret = return with exit code
Код выхода BPF сообщает ядру, копировать ли пакет в сокет или нет. Можно написать относительно небольшие программы BPF напрямую, используя setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,). (ВНИМАНИЕ: ядро принимает struct sock_fprog, а не struct bpf_program, не перепутайте их, иначе ваша программа не будет работать на некоторых платформах).
Для чего-то достаточно сложного, вы действительно хотите использовать libpcap. BPF ограничен в том, что он может делать, в частности, в количестве команд, которые он может выполнять для каждого пакета. libpcap позаботится о том, чтобы разбить сложный фильтр на две части, при этом ядро выполнит первый уровень фильтрации, а более мощный код пользовательского пространства отбросит пакеты, которые он на самом деле не хотел видеть.
libpcap также абстрагирует интерфейс ядра от кода вашего приложения. Linux и BSD используют похожие API, но Solaris требует DLPI, а Windows использует что-то другое.
Мне когда-то приходилось слушать необработанные кадры Ethernet, и в итоге я создал обертку для этого. Вызывая функцию с именем устройства, например eth0
Взамен я получил розетку, которая была в беспорядочном режиме. Что вам нужно сделать, это создать необработанный сокет и затем перевести его в беспорядочный режим. Вот как я это сделал.
int raw_init (const char *device)
{
struct ifreq ifr;
int raw_socket;
memset (&ifr, 0, sizeof (struct ifreq));
/* Open A Raw Socket */
if ((raw_socket = socket (PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 1)
{
printf ("ERROR: Could not open socket, Got #?\n");
exit (1);
}
/* Set the device to use */
strcpy (ifr.ifr_name, device);
/* Get the current flags that the device might have */
if (ioctl (raw_socket, SIOCGIFFLAGS, &ifr) == -1)
{
perror ("Error: Could not retrive the flags from the device.\n");
exit (1);
}
/* Set the old flags plus the IFF_PROMISC flag */
ifr.ifr_flags |= IFF_PROMISC;
if (ioctl (raw_socket, SIOCSIFFLAGS, &ifr) == -1)
{
perror ("Error: Could not set flag IFF_PROMISC");
exit (1);
}
printf ("Entering promiscuous mode\n");
/* Configure the device */
if (ioctl (raw_socket, SIOCGIFINDEX, &ifr) < 0)
{
perror ("Error: Error getting the device index.\n");
exit (1);
}
return raw_socket;
}
Затем, когда у вас есть сокет, вы можете просто использовать команду select для обработки пакетов по мере их поступления.
Вы можете использовать библиотеку pcap (см. http://www.tcpdump.org/pcap.htm), которая также используется tcpdump и Wireshark.
Почему бы вам не использовать что-то вроде WireShark?
Это открытый исходный код, так что, по крайней мере, вы можете извлечь из него несколько вещей, если не хотите просто использовать его.
WireShark в Linux имеет возможность собирать информацию заголовка PLCP (протокол конвергенции физического уровня).