Search code examples
c++function-pointers

callback method in pcap_loop


I am trying to use the pcap_loop function in libpcab library in Linux with this prototype:

int pcap_loop(pcap_t *, int, pcap_handler, u_char *);

pcap_pkthdr is a function pointer:

typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, const u_char *);

In my program, I have defined the following method in class SniffEthernet:

void SniffEthernet::got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet);

Now calling pcap_loop as below

pcap_loop(handle, num_packets, this->got_packet, NULL);

gives me the following compile-time error:

SniffEthernet.cc:139:58: error: cannot convert ‘VENTOS::SniffEthernet::got_packet’ from type ‘void (VENTOS::SniffEthernet::)(u_char*, const pcap_pkthdr*, const u_char*) {aka void (VENTOS::SniffEthernet::)(unsigned char*, const pcap_pkthdr*, const unsigned char*)}’ to type ‘pcap_handler {aka void (*)(unsigned char*, const pcap_pkthdr*, const unsigned char*)}’

What am I doing wrong here?

Edit: I found a similar post here.


Solution

  • Your callback function cannot be a member function (method). Don't forget that member functions always have the hidden parameter this.

    Your callback function must be either a namespace-level function or a static member of your class.

    If you want to have your object available for your CB function, you can use the user member (the last argument of pcap_loop(), first member of the callback function), with proper type casts, to pass arbitrary data, which, in your case, shall be the object you're using for the capture.

    The code below is incomplete and untested, but might give you an idea.

    class SniffEther {
        private:
            pcap_t *cap_handler;
            char errbuf[PCAP_ERRBUF_SIZE];
            /* capture-related data members (properties) */
    
        public:
            static friend void pkt_callback(u_char *user, const pcap_pkthdr *hdr, const u_char *bytes){
                SniffEther *sniffer=reinterpret_cast<SniffEther *>(user);
                /*
                    Process header and bytes.
    
                    You can call things like sniffer->somemethod(), and also
                    access sniffer->someproperty.
                */
            }
    
            // constructor
            SniffEther(const char *if_name){
                cap_handler=pcap_create(if_name, errbuf);
                if(!cap_handler)
                    throw runtime_error(errbuf);
                /* Set the many pcap_options (see pcap(3)). */
                if(pcap_activate(cap_handler)!=0){
                    string error(pcap_geterr(cap_handler));
                    pcap_close(cap_handler);
                    throw runtime_error(error);
                }
            }
    
            ~SniffEther(){
                if(cap_handler)
                    pcap_close(cap_handler);
            }
    
            void capture_loop(int pkt_count=-1){
                if(
                    pcap_loop(
                        cap_handler, pkt_count, pkt_callback,
                        reinterpret_cast<u_char *>(this)
                    )==-1
                )
                    throw runtime_error(pcap_geterr(cap_handler));
            }
    };