Querying Interfaces to find device with libpcap

908 views Asked by At

Ok, so I have systems that could be plugged up to any number of ethernet ports on the target computer. My goal was to loop through each device, collect around ~20 packets, or if it's getting no data then skip it, until I found the data I was looking for, and select that device as my "capture device". Seems simple enough. However, I quickly learned that libpcap will not simply timeout if no data is coming in. So, I tried setting my capture device to non-blocking mode with pcap_setnonblock. However, this causes my destination port that I read to become totally wacked out. Here's my code. Open to suggestions on what might be happening, or potentially even a better way to do this. Thanks

PS. Don't knock the Type1 Type2 variable names, they are for obfuscation.

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;
    }
}
1

There are 1 answers

2
AudioBubble On BEST ANSWER

In non-blocking mode, pcap_next_ex() will return 0 if there are no packets available to read, and will NOT return any packet information, so, if it returned 0, you should not look at anything that header or packet_data points to.

I.e., do

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;
        }
    }
}

Note, however, that your program will continuously spin, consuming CPU, in that loop, forever. This means that you'll never look past the first device.

What you probably really want to do is:

  • open all the devices, putting their pcap_t *s into an array, and put them into non-blocking mode;
  • for each of the devices, call pcap_get_selectable_fd() on UN*X and pcap_getevent() on Windows, and save the result of that call into an array (you can have parallel arrays, or an array of structures with the pcap_t * and the int or HANDLE as members);
  • in the loop, use select() or poll() on all of the ints from pcap_get_selectable_fd() on UN*X or WaitForMultipleObjects() on all of the HANDLEs from pcap_getevent() on Windows and, when select()/poll()/WaitForMultipleObjects() returns, try reading a packet from each of the pcap_t *s using pcap_next_ex(), and process it if you get a packet.

That way, you scan on all devices in parallel, and don't spin the CPU.