Home > Net >  LibPCap pcap_loop() does not return any packets
LibPCap pcap_loop() does not return any packets

Time:04-01

I have problem with my C program, everything is fine until I start capturing packets. It should log, but the handler void in pcap_loop() isn't. Also I have another problem with the input in fgets on line 98 when it gets passed into the pcap create void it will not find the interface by the name. When I hardcode the string it works.

/* 
    Compile and run it using GCC ->
    gcc main.c -o output -L/usr/include -lpcap && ./output
*/
#include <stdio.h>
#include <pcap.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define __NEWLINE__ "\r\r\n"
#define __SEPARATOR__ "------------------------"

/*
    Print all flags in human readable string 
    instead of hexadecimal number
*/
char* get_interface_flags(bpf_u_int32* decimal_flags) {
    char* flag_string = (char*)malloc(sizeof(char) * 30);
    strcpy(flag_string, "\0");

    const int flag_names[9] = {
        PCAP_IF_LOOPBACK, 
        PCAP_IF_UP, 
        PCAP_IF_RUNNING, 
        PCAP_IF_WIRELESS
    };

    const char* flag_values[9] = {
        "LOOPBACK",
        "UP",
        "RUNNING",
        "WIRELESS"
    };

    for (size_t i = 0; i < (sizeof(flag_names) / sizeof(flag_names[0])); i  )
    {
        if(flag_names[i] & *decimal_flags) {
            if(strlen(flag_string) != 0) strcat(flag_string, ", ");
            strcat(flag_string, flag_values[i]);
        }
    }
    
    return flag_string;
}

/*
    Print available information about network interface
    This recursive function can be called again
    if there is more than one interface present
*/
void print_interface(pcap_if_t* device) {
    // Device parameters
    bpf_u_int32* flags = &device->flags;
    char* device_name = device->name;
    char* device_desc = device->description;
    char* flags_string = get_interface_flags(flags);

    // Device addresses  
    pcap_addr_t* addr = device->addresses;

    // We'll print devices with only 1 flag or more to avoid interfaces that aren't up
    if(strlen(flags_string) != 0) {
        fprintf(stdout, "%s: FLAGS(%lu)<%s>%s", device_name, strlen(flags_string), flags_string, __NEWLINE__);    
        fprintf(stdout, "%s%s", device_desc, __NEWLINE__);
        fprintf(stdout, "%s%s", __SEPARATOR__, __NEWLINE__);
    }

    // Check if there is another device to print, if so call the recursive function again
    if(device[0].next != NULL) print_interface(device->next);
    
    free(flags_string);
}

// Let user select the network interface that will capture packets
char* get_selected_interface() {
    char* selected_interface = (char*)malloc(sizeof(char) * 20);
    char* device_name;
    char* ip_addr;
    char errbuf[PCAP_ERRBUF_SIZE];
    pcap_if_t* alldevs;

    // Get all available network interfaces, returns PCAP_ERROR on failure or 0 on success
    int devices_succefully_returned = pcap_findalldevs(&alldevs, errbuf);

    // If there was an error while fetching interfaces, return empty char
    if(devices_succefully_returned == PCAP_ERROR) {
        printf("%s", errbuf);
        return '\0';
    }

    print_interface(alldevs);

    fprintf(stdout, "Name of the network interface to use: ");
    fgets(selected_interface, 20, stdin);

    return selected_interface;
}

void captured_packet(u_char *args, const struct pcap_pkthdr *hdr, const u_char *pkt) {
    fprintf(stdout, "log");
}

void capture(char* device) {
    char* errbuf[PCAP_ERRBUF_SIZE];

    /*
        Will create a packet capture handle
        Returns NULL if handle can't be created, in this case
        we'll print out the error buffer and exit the program with 1 status code
    */
    pcap_t* created = pcap_create("eth0", *errbuf);

    if(created == NULL) {
        fprintf(stderr, "%s%s", *errbuf, __NEWLINE__);
        return exit(EXIT_FAILURE);
    };

    int activation_status = pcap_activate(created);

    if(activation_status == PCAP_ERROR || activation_status != 0) {
        fprintf(stderr, "%s", pcap_geterr(created));
        exit(EXIT_FAILURE);
    }

    /*
        Interface was activated so start capturing incoming packets
        capture_packet will handle incoming packets.
        We'll capture infinite amount of packets unless user provides a count
    */
    fprintf(stdout, "Interface activated!%s", __NEWLINE__);

    int loop_status = pcap_loop(created, -1, captured_packet, NULL);

    switch (loop_status) 
    {
        case PCAP_ERROR_BREAK:
            fprintf(stderr, "Loop was finished beacause of breakloop that was called.");
            break;
    
        case PCAP_ERROR_NOT_ACTIVATED:
            fprintf(stderr, "Device wasn't activated before it started capturing.");
            break;

        case 0:
            fprintf(stderr, "Loop was terminated due to exhaustion of count");
            break;

        default:
            fprintf(stderr, "Some error happened while trying to loop through packets -> %s", pcap_geterr(created));
            break;
    }

    // Program was successfully closed, exit..
    pcap_close(created);
    exit(0);
}

int main(int argc, char **argv) {
    if(getuid() != 0) {
        fprintf(stderr, "Please make sure to run this tool as root!%s", __NEWLINE__);
        return 1;
    }

    char* selected = get_selected_interface();
    capture(selected);
    free(selected);
    return 0;
}

I tried everything, it should log unlimited number of packets on the "eth0" interface but nothing happens.

CodePudding user response:

I have problem with my C program, everything is fine until I start capturing packets. It should log, but the handler void in pcap_loop() isn't.

To quote the pcap(3PCAP) man page:

   packet buffer timeout
          If, when capturing,  packets  are  delivered  as  soon  as  they
          arrive,  the  application capturing the packets will be woken up
          for each packet as it arrives, and might have  to  make  one  or
          more calls to the operating system to fetch each packet.
    
          If,  instead,  packets are not delivered as soon as they arrive,
          but are delivered after a short delay (called a  "packet  buffer
          timeout"),  more  than  one packet can be accumulated before the
          packets are delivered, so that a single wakeup would be done for
          multiple  packets,  and  each set of calls made to the operating
          system would supply  multiple  packets,  rather  than  a  single
          packet.  This reduces the per-packet CPU overhead if packets are
          arriving at a high rate, increasing the number  of  packets  per
          second that can be captured.
    
          The  packet  buffer  timeout  is required so that an application
          won't wait for the operating system's capture buffer to fill  up
          before  packets  are  delivered; if packets are arriving slowly,
          that wait could take an arbitrarily long period of time.
    
          Not all platforms support a packet buffer timeout; on  platforms
          that  don't, the packet buffer timeout is ignored.  A zero value
          for the timeout, on platforms that support a packet buffer time-
          out,  will  cause a read to wait forever to allow enough packets
          to arrive, with no timeout.  A negative value  is  invalid;  the
          result  of  setting  the  timeout  to a negative value is unpre-
          dictable.
    
          NOTE: the packet buffer timeout cannot be used  to  cause  calls
          that  read  packets  to  return within a limited period of time,
          because, on some platforms, the packet buffer timeout isn't sup-
          ported,  and,  on other platforms, the timer doesn't start until
          at least one packet arrives.  This means that the packet  buffer
          timeout  should  NOT  be  used,  for  example, in an interactive
          application to allow the packet capture  loop  to  ``poll''  for
          user  input  periodically,  as  there's no guarantee that a call
          reading packets will return after the timeout expires even if no
          packets have arrived.
    
          The packet buffer timeout is set with pcap_set_timeout().

and the pcap_set_timeout(3PCAP) man page:

   pcap_set_timeout() sets the packet buffer timeout that will be used  on
   a  capture  handle  when  the handle is activated to to_ms, which is in
   units of milliseconds.  (See pcap(3PCAP)  for  an  explanation  of  the
   packet buffer timeout.)

   The  behavior,  if the timeout isn't specified, is undefined, as is the
   behavior if the timeout is set to zero or to a negative value.  We rec-
   ommend  always setting the timeout to a non-zero value unless immediate
   mode is set, in which case the timeout has no effect.

You haven't called pcap_set_timeout(); on some platforms, that can cause pcap_loop() to wait until it gets an entire buffer full of packet, which could take a significant amount of time.

Set the timeout to 100, which means 100 milliseconds or a tenth of a second.

Also I have another problem with the input in fgets on line 98 when it gets passed into the pcap create void it will not find the interface by the name. When I hardcode the string it works.

To quote the fgets(3) man page (this is on macOS, and you're probably working on Linux, given the "eth0" name, but all of the stuff I'm quoting from the man pages applies to macOS, Linux, and other UN*Xes):

     The fgets() function reads at most one less than the number of characters
     specified by size from the given stream and stores them in the string
     str.  Reading stops when a newline character is found, at end-of-file or
     error.  The newline, if any, is retained.  If any characters are read and
     there is no error, a `\0' character is appended to end the string.

The important part here is "The newline, if any, is retained." - this means that, if the user types "eth0" and then hits the Return key, the string read in will be "eth0\n", complete with a newline character. The name of the interface is "eth0", not "eth0\n"; you will have to remove any trailing newline from the string before using it.

  • Related