从混杂的网络设备中读取
我想编写一个用于无线流量的实时分析工具。
有谁知道如何从C中的混杂(或者嗅探)设备读取数据?
我知道我们需要具有root用户权限才能执行此操作。我想知道是否有人知道执行此操作所需的功能。普通的插座在这里似乎没有意义。
解决方案
我们为什么不使用WireShark之类的东西?
它是开源的,因此,如果我们不想仅使用它,至少可以从中学习一些东西。
我们可以使用pcap库(请参阅http://www.tcpdump.org/pcap.htm),该库也被tcpdump和Wireshark使用。
在Linux上,我们使用PF_PACKET套接字从原始设备读取数据,例如以混杂模式运行的以太网接口:
s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))
这会将收到的每个数据包的副本发送到套接字。但是,我们很可能并不需要所有数据包。内核可以使用BPF(伯克利分组过滤器)执行第一级过滤。 BPF本质上是一个基于堆栈的虚拟机:它处理一小部分指令,例如:
ldh = load halfword (from packet) jeq = jump if equal ret = return with exit code
BPF的退出代码告诉内核是否将数据包复制到套接字。可以使用setsockopt(s,SOL_SOCKET,SO_ATTACH_FILTER,)直接编写相对较小的BPF程序。 (警告:内核采用的是sock_fprog结构,而不是bpf_program结构,请勿将它们混为一谈,否则程序将无法在某些平台上运行)。
对于任何相当复杂的东西,我们真的想使用libpcap。 BPF的功能受到限制,尤其是每个数据包可以执行的指令数受到限制。 libpcap将负责将一个复杂的过滤器分为两部分,内核执行第一级过滤,而功能更强大的用户空间代码将丢弃实际上不希望看到的数据包。
libpcap还从应用程序代码中抽象出内核接口。 Linux和BSD使用类似的API,但是Solaris需要DLPI,而Windows使用其他东西。
我曾经不得不监听原始的以太网帧,并最终为此创建了一个包装器。通过使用设备名称(例如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来处理到达的数据包。