seladb / PcapPlusPlus

PcapPlusPlus is a multiplatform C++ library for capturing, parsing and crafting of network packets. It is designed to be efficient, powerful and easy to use. It provides C++ wrappers for the most popular packet processing engines such as libpcap, Npcap, WinPcap, DPDK, AF_XDP and PF_RING.
https://pcapplusplus.github.io/
The Unlicense
2.68k stars 648 forks source link

Error setting the capture mode for device - asynchronous packet capture using a callback function #680

Closed untyper closed 3 years ago

untyper commented 3 years ago

So I tried the callback way to asynchronously capture packets from all devices following the example from here, but it doesn't work for some reason;

#include <iostream>
#include <vector>
#include <chrono>
#include <thread>

#include "PcapLiveDeviceList.h"

/**
* A struct for collecting packet statistics
*/
struct PacketStats
{
    int ethPacketCount;
    int ipv4PacketCount;
    int ipv6PacketCount;
    int tcpPacketCount;
    int udpPacketCount;
    int dnsPacketCount;
    int httpPacketCount;
    int sslPacketCount;

    /**
    * Clear all stats
    */
    void clear() { ethPacketCount = 0; ipv4PacketCount = 0; ipv6PacketCount = 0; tcpPacketCount = 0; udpPacketCount = 0; tcpPacketCount = 0; dnsPacketCount = 0; httpPacketCount = 0; sslPacketCount = 0; }

    /**
    * C'tor
    */
    PacketStats() { clear(); }

    /**
    * Collect stats from a packet
    */
    void consumePacket(pcpp::Packet& packet)
    {
        if (packet.isPacketOfType(pcpp::Ethernet))
            ethPacketCount++;
        if (packet.isPacketOfType(pcpp::IPv4))
            ipv4PacketCount++;
        if (packet.isPacketOfType(pcpp::IPv6))
            ipv6PacketCount++;
        if (packet.isPacketOfType(pcpp::TCP))
            tcpPacketCount++;
        if (packet.isPacketOfType(pcpp::UDP))
            udpPacketCount++;
        if (packet.isPacketOfType(pcpp::DNS))
            dnsPacketCount++;
        if (packet.isPacketOfType(pcpp::HTTP))
            httpPacketCount++;
        if (packet.isPacketOfType(pcpp::SSL))
            sslPacketCount++;
    }

    /**
    * Print stats to console
    */
    void printToConsole()
    {
        printf("Ethernet packet count: %d\n", ethPacketCount);
        printf("IPv4 packet count:     %d\n", ipv4PacketCount);
        printf("IPv6 packet count:     %d\n", ipv6PacketCount);
        printf("TCP packet count:      %d\n", tcpPacketCount);
        printf("UDP packet count:      %d\n", udpPacketCount);
        printf("DNS packet count:      %d\n", dnsPacketCount);
        printf("HTTP packet count:     %d\n", httpPacketCount);
        printf("SSL packet count:      %d\n", sslPacketCount);
    }
};

static void onPacketArrives(pcpp::RawPacket* rawPacket, pcpp::PcapLiveDevice* device, void* cookie) {
    // extract the stats object form the cookie
    PacketStats* stats = (PacketStats*)cookie;

    // parsed the raw packet
    pcpp::Packet parsedPacket(rawPacket);

    // collect stats from packet
    stats->consumePacket(parsedPacket);
}

int main() {
    // Store list of devices to later determine the active device
    std::vector<pcpp::PcapLiveDevice*> deviceList = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList();

    PacketStats stats;

    // Loop to open all devices for capturing
    for (const auto& device : deviceList) {
        // Open device for capturing
        if (device->open()) {
            device->startCapture(onPacketArrives, &stats);
        }
    }

    // Sleep 10 seconds and then stop capture
    std::this_thread::sleep_for(std::chrono::seconds(10));

    // Loop to close all devices currently capturing
    for (const auto& device : deviceList) {
        device->stopCapture();
        device->close();
    }

    std::cout << "\nResults:" << std::endl;
    stats.printToConsole();

    return 0;
}

The above produces a capture device error for all devices;

Error setting the capture mode for device '\Device\NPF_{74E88B32-98F6-46AB-B837-99F1DFA7906E}'
Error setting the capture mode for device '\Device\NPF_{1E01DB7C-9AF4-4E3F-A8B0-0655D640BE4D}'
Error setting the capture mode for device '\Device\NPF_{468C5ED4-50A3-482B-9793-228B626648F3}'
Error setting the capture mode for device '\Device\NPF_{AC56EB90-D4BE-44D9-B522-324D7B30B743}'
Error setting the capture mode for device '\Device\NPF_{C40821EC-32A7-4EC0-B66C-4BA1F7D57646}'
Error setting the capture mode for device '\Device\NPF_{3B408163-8FA1-4617-A360-60369EC2C127}'
Error setting the capture mode for device '\Device\NPF_Loopback'
Unknown NdisMedium value 19, defaulting to DLT_EN10MB
Unknown NdisMedium value 19, defaulting to DLT_EN10MB
Error setting the capture mode for device '\Device\NPF_{8B490542-3A9B-41EB-9E20-8BC942D64864}'

Results:
Ethernet packet count: 0
IPv4 packet count:     0
IPv6 packet count:     0
TCP packet count:      0
UDP packet count:      0
DNS packet count:      0
HTTP packet count:     0
SSL packet count:      0

But it works just fine by filling the vector asynchronously;

#include <iostream>
#include <vector>
#include <chrono>
#include <thread>

#include "PcapLiveDeviceList.h"

/**
* A struct for collecting packet statistics
*/
struct PacketStats
{
    int ethPacketCount;
    int ipv4PacketCount;
    int ipv6PacketCount;
    int tcpPacketCount;
    int udpPacketCount;
    int dnsPacketCount;
    int httpPacketCount;
    int sslPacketCount;

    /**
    * Clear all stats
    */
    void clear() { ethPacketCount = 0; ipv4PacketCount = 0; ipv6PacketCount = 0; tcpPacketCount = 0; udpPacketCount = 0; tcpPacketCount = 0; dnsPacketCount = 0; httpPacketCount = 0; sslPacketCount = 0; }

    /**
    * C'tor
    */
    PacketStats() { clear(); }

    /**
    * Collect stats from a packet
    */
    void consumePacket(pcpp::Packet& packet)
    {
        if (packet.isPacketOfType(pcpp::Ethernet))
            ethPacketCount++;
        if (packet.isPacketOfType(pcpp::IPv4))
            ipv4PacketCount++;
        if (packet.isPacketOfType(pcpp::IPv6))
            ipv6PacketCount++;
        if (packet.isPacketOfType(pcpp::TCP))
            tcpPacketCount++;
        if (packet.isPacketOfType(pcpp::UDP))
            udpPacketCount++;
        if (packet.isPacketOfType(pcpp::DNS))
            dnsPacketCount++;
        if (packet.isPacketOfType(pcpp::HTTP))
            httpPacketCount++;
        if (packet.isPacketOfType(pcpp::SSL))
            sslPacketCount++;
    }

    /**
    * Print stats to console
    */
    void printToConsole()
    {
        printf("Ethernet packet count: %d\n", ethPacketCount);
        printf("IPv4 packet count:     %d\n", ipv4PacketCount);
        printf("IPv6 packet count:     %d\n", ipv6PacketCount);
        printf("TCP packet count:      %d\n", tcpPacketCount);
        printf("UDP packet count:      %d\n", udpPacketCount);
        printf("DNS packet count:      %d\n", dnsPacketCount);
        printf("HTTP packet count:     %d\n", httpPacketCount);
        printf("SSL packet count:      %d\n", sslPacketCount);
    }
};

int main() {
    // Store list of devices to later determine the active device
    std::vector<pcpp::PcapLiveDevice*> deviceList = pcpp::PcapLiveDeviceList::getInstance().getPcapLiveDevicesList();

    // List of packets stored asynchronously
    pcpp::RawPacketVector packetVector;

    PacketStats stats;

    // Loop to open all devices for capturing
    for (const auto& device : deviceList) {
        // Open device for capturing
        if (device->open()) {
            device->startCapture(packetVector);
        }
    }

    // Sleep 10 seconds and then stop capture
    std::this_thread::sleep_for(std::chrono::seconds(10));

    // Loop to close all devices currently capturing
    for (const auto& device : deviceList) {
        device->stopCapture();
        device->close();
    }

    for (const auto& rawPacket : packetVector) {
        pcpp::Packet parsedPacket(rawPacket);
        stats.consumePacket(parsedPacket);
    }

    std::cout << "\nResults:" << std::endl;
    stats.printToConsole();

    return 0;
}
Results:
Ethernet packet count: 12915
IPv4 packet count:     12919
IPv6 packet count:     38
TCP packet count:      7355
UDP packet count:      5602
DNS packet count:      28
HTTP packet count:     0
SSL packet count:      27

It also works with the blocking callback way of capturing packets. Am I missing something? Why is the async callback not working? Any ideas?

I built PcapPlusPlus from the source of the May 2021 Release using MSYS2 Mingw-w64 GCC i686-w64-mingw32 version 10.3.0 OS: Windows 10 Enterprise version 20H2 build 19042.1052

Edit: Alternatively, is there a better way to capture packets from all interfaces?

seladb commented 3 years ago

hi @untyper , thanks for reporting this issue! I'm not sure why this is happening, did you manage to get it working with one interface?

x200502126115 commented 3 years ago

I have the same problem in windows 10, msvc 2017 , using the example Tutorial-LiveTraffic . I choose only one interface.

seladb commented 3 years ago

@x200502126115 if it doesn't work with one interface it's probably a different issue. Can you debug and provide more details?

oddaolse commented 3 years ago

I get "Error setting the capture mode for device" as well. I get it when I try to run the SSLAnalyzer example. I have previously built on Linux. Worked. Also built on WSL2 - worked as well. Have now built on Windows. Capturing with Wireshark works.

seladb commented 3 years ago

@oddaolse are you using WinPcap or Npcap? Also, which Windows version do you have?

seladb commented 3 years ago

@oddaolse @x200502126115 @untyper I'm trying to narrow down the problem, since I can't reproduce it on my machine.

Also, if someone can debug it and help finding the root cause that'd be a great help, thanks!

seladb commented 3 years ago

I'm not sure how to further debug the issue without this information so I'll close it now. Please reopen if needed

siilky commented 2 years ago

bumping this up, It seems to be a sort of regression of npcap 1.55. I retried this using npcap 1.35, and it seems to work ok. The error is apparently caused by a code inside of pcap_setmode(m_PcapDescriptor, MODE_CAPT) at if (AdapterObject->Flags == INFO_FLAG_NDIS_ADAPTER) { Result = (BOOLEAN)DeviceIoControl(AdapterObject->hFile,BIOCSMODE,&mode,4,NULL,0,&BytesReturned,NULL); } in https://github.com/nmap/npcap/blob/master/packetWin7/Dll/Packet32.cpp

seladb commented 2 years ago

Thanks @siilky ! so I understand it only happens with newer version of Npcap? Did you try with WinPcap as well? If it's a Npcap bug, maybe you can open an issue in their repo?