查询接口以使用 libpcap 查找设备

Querying Interfaces to find device with libpcap

好的,所以我的系统可以插入目标计算机上任意数量的以太网端口。我的目标是遍历每个设备,收集大约 20 个数据包,或者如果没有数据则跳过它,直到找到我要查找的数据,并且 select 该设备作为我的 "capture device" .看起来很简单。但是,我很快了解到,如果没有数据进入,libpcap 不会简单地超时。因此,我尝试使用 pcap_setnonblock 将我的捕获设备设置为非阻塞模式。但是,这会导致我阅读的目标端口完全崩溃。这是我的代码。愿意接受有关可能发生的事情的建议,或者甚至是更好的方法。谢谢

PS。 Type1 Type2变量名不要敲,是用来混淆的

pcap_if_t *alldevs;
pcap_if_t *d;
pcap_t *fp;
struct pcap_pkthdr *header;
const u_char *pkt_data;
char errbuf[PCAP_ERRBUF_SIZE];
const int FIND_DEVICE_PACKET_LIMIT = 20;

#ifdef WIN32
    if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING, NULL, &alldevs, errbuf) == -1) 
#else
    if (pcap_findalldevs(&alldevs, errbuf) == -1)
#endif
{
    cerr << "Error in pcap_findalldevs_ex: " << errbuf << endl;
    return;
}

// For each device, capture until timeout
//   or until a matching packet is found.
for(d=alldevs; d; d=d->next)
{        
    if ((fp = pcap_open_live(d->name,
                1514 /*snaplen*/,
                1 /*flags, 1=promiscuous, 0=not promiscuous*/,
                20 /*read timeout*/,
                errbuf)
                ) == NULL) 
    {
        cerr << endl << "Unable to open the adapter." << endl;
        continue;
    }

    int res = 0;
    int packetCounter = 0;


    pcap_setnonblock(fp, true, errbuf);

    // Capture at most FIND_DEVICE_PACKET_LIMIT packets
    //    to determine whether scanner is sending packets
    //    or no scanner found.
    while ((res = pcap_next_ex(fp, &header, &pkt_data)) >=0) {
        struct iphdr *iph = (struct iphdr *)(pkt_data +  sizeof(struct ethhdr));
        struct udphdr *udph = (struct udphdr*)(pkt_data + (iph->ihl*4)  + sizeof(struct ethhdr));
        u_int destPort = ntohs(udph->dest);

        if (destPort==TYPE1_DATA_PORT || destPort==TYPE1_GPS_PORT) {
            detectedScanners->type1 = true;
        } else if (destPort==TYPE2_DATA_PORT || destPort==TYPE2_STATUS_PORT || destPort==TYPE2_NMEA_PORT) {
            detectedScanners->type2 = true;
        }

        if (++packetCounter > FIND_DEVICE_PACKET_LIMIT) {
            break;
        }
    }

    if (detectedScanners->type1==true || detectedScanners->type2==true) {
        *interface = d->name;
        break;
    }
}

在非阻塞模式下,如果没有数据包可读取,pcap_next_ex() 将 return 0,并且将 NOT return任何数据包信息,因此,如果它 returned 为 0,您应该 而不是 查看 headerpacket_data 指向的任何内容。

即做

while ((res = pcap_next_ex(fp, &header, &pkt_data)) >=0) {
    if (res != 0) {
        struct iphdr *iph = (struct iphdr *)(pkt_data +  sizeof(struct ethhdr));
        struct udphdr *udph = (struct udphdr*)(pkt_data + (iph->ihl*4)  + sizeof(struct ethhdr));
        u_int destPort = ntohs(udph->dest);

        if (destPort==TYPE1_DATA_PORT || destPort==TYPE1_GPS_PORT) {
            detectedScanners->type1 = true;
        } else if (destPort==TYPE2_DATA_PORT || destPort==TYPE2_STATUS_PORT || destPort==TYPE2_NMEA_PORT) {
            detectedScanners->type2 = true;
        }

        if (++packetCounter > FIND_DEVICE_PACKET_LIMIT) {
            break;
        }
    }
}

但是请注意,您的程序将不断旋转,在该循环中消耗 CPU,永远。这意味着您永远不会错过第一台设备。

您可能真正想做的是:

  • 打开所有个设备,将它们的pcap_t *放入一个数组中,并设置为非阻塞模式;
  • 对于每个设备,在 UN*X 上调用 pcap_get_selectable_fd() 并在 Windows 上调用 pcap_getevent(),并将该调用的结果保存到数组中(您可以使用并行数组, 或以 pcap_t *intHANDLE 作为成员的结构数组);
  • 在循环中,对 UN*X 上 pcap_get_selectable_fd() 的所有 int 或对所有 WaitForMultipleObjects() 使用 select()poll()来自 Windows 上 pcap_getevent()HANDLEs,当 select()/poll()/WaitForMultipleObjects() returns 时,尝试读取数据包来自每个pcap_t *s使用pcap_next_ex(),如果你得到一个数据包就处理它。

这样,您就可以并行扫描所有设备,并且不会旋转 CPU。