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;
}
}
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 thatheader
orpacket_data
points to.I.e., do
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:
pcap_t *
s into an array, and put them into non-blocking mode;pcap_get_selectable_fd()
on UN*X andpcap_getevent()
on Windows, and save the result of that call into an array (you can have parallel arrays, or an array of structures with thepcap_t *
and theint
orHANDLE
as members);select()
orpoll()
on all of theint
s frompcap_get_selectable_fd()
on UN*X orWaitForMultipleObjects()
on all of theHANDLE
s frompcap_getevent()
on Windows and, whenselect()
/poll()
/WaitForMultipleObjects()
returns, try reading a packet from each of thepcap_t *
s usingpcap_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.