c++linkerlibpcappacket-sniffers

Undefined Reference to `pcap_init' - Libpcap


I'm currently trying to set up a simple packet sniffer with libpcap and facing a lot of confusion over this linker error.

I cloned the most recent version from github (1.11.0-PRE-GIT) and successfully did the configure, make and make install steps outlined in the installation instructions.

My script is as follows:

#include <iostream>
#include <pcap/pcap.h>
#include <string>
#include <cstdlib>


using namespace std;

int main(int argc, char *argv[])
{
    //Set up error buffer
    char errbuf[PCAP_ERRBUF_SIZE];

    // Check Libpcap version number
    cout << pcap_lib_version() << endl << endl;

    //Initialize the library for local charactr encoding         
    pcap_init(PCAP_CHAR_ENC_LOCAL, errbuf);
    
    return 0;
}

But when I try to compile with the command below I get the error:

g++ csniff.cc -o csniff -lpcap

/usr/bin/ld: /tmp/ccAPLFoh.o: in function `main':
csniff.cc:(.text+0x79): undefined reference to `pcap_init'
collect2: error: ld returned 1 exit status

I've also checked the pcap.h files present in both usr/include and usr/local/include and they both contain a prototype for the pcap_init function that looks like this:

#define PCAP_CHAR_ENC_LOCAL 0x00000000U /* strings are in the local 
character encoding */
#define PCAP_CHAR_ENC_UTF_8 0x00000001U /* strings are in UTF-8 */

PCAP_AVAILABLE_1_10
PCAP_API int    pcap_init(unsigned int, char *);

One thing I have noticed though is that when I comment out the line with pcap_init and use the script to print the version number I get

libpcap version 1.9.1 (with TPACKET_V3)

Any pointers would be much appreciated!

Edit: runnning Ubuntu 20.04.3 LTS


Solution

  • Your operating system already comes with libpcap - version 1.9.1. Most Linux distributions do, as do the *BSDs, macOS, and some commercial UN*Xes.

    You compiled and installed a newer version of libpcap, so you had two versions of the library file - the 1.9.1 that comes with the system, in the system library directory, because it comes with the OS, and the 1.11.0-PRE-GIT and you compiled and installed, probably in /usr/local/lib.

    You also had one version of the libpcap header files - the 1.11.0-PRE-GIT version - in /usr/local/include/pcap, because you installed it. You did not have the 1.9.1 version of the header files, because, like many Linux distributions, a separate "development" package has to be installed in order to get the header files.

    When you compiled your program, you didn't tell it where to look for header files or libraries. It found the header files in /usr/local/include/pcap - i.e., the 1.11.0-PRE-GIT header files - and found the library in the system library directory - i.e., the 1.9.1 library.

    This causes problems, because the header files included a declaration of pcap_init(), so the compiler didn't print a warning about pcap_init() not being declared, but the library file doesn't include the pcap_init() function, so the linker printed an error about pcap_init() not being found in libpcap.

    Removing the call to pcap_init() meant that the linker didn't try to find pcap_init(), and thus didn't fail.

    If you hadn't built and installed libpcap 1.11.0-PRE-GIT, and you had installed the libpcap-dev package, your system would have the headers and library for 1.9.1, and compiling your program - without the call to pcap_init() - would find the headers for 1.9.1, so it can compile, and the library for 1.9.1, so it will link.

    If, however, you want to build with 1.11.0-PRE-GIT, then you will need to tell the compiler where to find the libraries; you might also have to tell it where to find the headers.

    If you don't have the libpcap-dev package installed, then you don't need to tell it where to find the headers, as it will find the 1.11.0-PRE-GIT ones you installed; however, if the libpcap-dev package is installed, you may have to add the flag -I /usr/local/include to the compiler command, to make sure it finds the headers in /usr/local/include rather than the system include directory.

    To make sure it finds the 1.11.0-PRE-GIT version of the libraries, you will have to add the flag -L /usr/local/lib to the compiler command as well - or set LD_LIBRARY_PATH so that the linker finds the 1.11.0-PRE-GIT version.

    If you build with 1.11.0-PRE-GIT, you can use pcap_init(). You don't need to use pcap_init() unless you want to run the program on some UN*X and on Windows and you want all strings to be in UTF-8 (on Windows, strings would, by default, be treated as being in the "local code page"). To quote the DESCRIPTION section of the pcap_init() man page:

       pcap_init()  is  used  to  initialize the Packet Capture library.  opts
       specifies options for the library; currently, the options are:
    
       PCAP_CHAR_ENC_LOCAL
              Treat all strings supplied as arguments, and return all  strings
              to the caller, as being in the local character encoding.
    
       PCAP_CHAR_ENC_UTF_8
              Treat  all strings supplied as arguments, and return all strings
              to the caller, as being in UTF‐8.
    
       On UNIX‐like systems, the local character encoding  is  assumed  to  be
       UTF‐8, so no character encoding transformations are done.
    
       On Windows, the local character encoding is the local ANSI code page.
    
       If pcap_init() is not called, strings are treated as being in the local
       ANSI code page on Windows, pcap_lookupdev(3PCAP) will succeed if  there
       is  a  device  on  which  to  capture,  and pcap_create(3PCAP) makes an
       attempt to check whether the string passed as an argument is a UTF‐16LE
       string  ‐  note that this attempt is unsafe, as it may run past the end
       of the string ‐ to handle pcap_lookupdev() returning a UTF‐16LE string.
       Programs   that   don’t  call  pcap_init()  should,  on  Windows,  call
       pcap_wsockinit() to  initialize  Winsock;  this  is  not  necessary  if
       pcap_init() is called, as pcap_init() will initialize Winsock itself on
       Windows.