Two pcap_compile() on one device at same time?

1.4k views Asked by At

I have two threads and each one has packet capture from the same deviсe at the same time but the program crashes when the second thread reaches the pcap_compile() function. Also each thread has their own variables and don't use global. It seems that they get the same handle of the device, therefore the program crashes. Why do I need two threads? Because I want to seperate packets on the sent and on the recived by specified pcap filter. So how do I solve this? Or is it better to use one thread and sort manually the sent and the received packets by using the address from tcp header?

2

There are 2 answers

0
Anya Shenanigans On BEST ANSWER

pcap_compile is not thread safe. You must surround all calls to it that may be encountered by separate threads with a critical section/mutex to prevent errors because of non thread-safe state within the parser that compiles the expression (for the gory details, it uses YACC to create code for parsing the expression and the code generated for that is eminently not thread safe).

You need to explicitly open the device once per thread that you're planning on using for the capture, if you reuse the same device handle across multiple threads then it will simply not do what you're asking for. You should open the pcap handle within the thread that you're planning on using it, so each thread that's planning on doing capture should do it's own pcap_open.

to guard the call to pcap_compile with a Critical Section, you could create a simple wrapper (C++ wrapper of windows critical section):

class lock_interface {
public:
    virtual void lock() = 0;
    virtual void unlock() = 0;
};

class cs : public lock_interface {
    CRITICAL_SECTION crit;
public:
    cs() { InitializeCriticalSection(&crit); }
    ~cs() { DeleteCriticalSection(&crit); }
    virtual void lock() {
        EnterCriticalSection(&crit);
    }
    virtual void unlock() {
        LeaveCriticalSection(&crit);
    }
private:
    cs(const locker &);
    cs &operator=(const cs &);
};

class locker {
    lock_interface &m_ref;
public:
    locker(lock_interface &ref) : m_ref(ref) { m_ref.lock(); }
    ~locker() { m_ref.unlock(); }
private:
    locker(const locker &);
    locker &operator=(const locker &);
};

static cs section;

int
wrapped_pcap_compile(pcap_t *p, struct bpf_program *fp, const char *str, int optimize, bpf_u_int32 netmask)
{
    locker locked(section);
    pcap_compile(p, fp, str, optimize, netmask);
}
0
NutCracker On

If you are using C++11, you can have something like:

int thread_safe_pcap_compile_nopcap(int snap_len, int link_type,
                                    struct bpf_program *fp, char const *str,
                                    int optimize, bpf_u_int32 netmask) {
    static std::mutex mtx;
    std::lock_guard<std::mutex> lock(mtx);
    return pcap_compile_nopcap(snap_len, link_type, fp, str, optimize, netmask);
}

It is similar for pcap_compile function.