pcap_next вызывает ошибку сегментации

#c #libpcap

Вопрос:

Я пишу простую программу захвата пакетов, которая захватывает три пакета с помощью функций pcap. Но он выходит из строя из-за ошибки сегментации. Ниже приведен исходный код программы.

 #include <pcap.h>
#include <stdlib.h>
#include <stdio.h>
#include "dump.h"

void pcap_fatal(const char *failed_in, const char *errbuf){
    printf("Fata Error in %s: %sn", failed_in, errbuf);
    exit(1);
}

int main(){
    struct pcap_pkthdr *header;
    const u_char *packet;
    char errbuf[PCAP_ERRBUF_SIZE];
    char *device;
    pcap_t *pcap_handle;
    int i;

    /*look for a device to capture packets*/
    device = pcap_lookupdev(errbuf);
    if(device == NULL)
        pcap_fatal("pcap_lookupdev", errbuf);
    printf("Sniffing on device %sn", device);

    /*opens the packet capturing device*/
    pcap_handle = pcap_open_live(device, 4096, 1, 0, errbuf);
    if(pcap_handle < 0){
        perror("ERROR: while opening pcap device");
        exit(1);
    }

    for(i=0;i<3;i  ){
        printf("n===packet %d===n", i 1);
        packet = pcap_next(pcap_handle, header);
        printf("Got a %d bute packetn", header->len);
        dump(packet, header->len);
    }
    
    pcap_close(pcap_handle);
}
 

Можете ли вы понять, что происходит ??

Комментарии:

1. struct pcap_pkthdr *header никогда не инициализируется! Используйте struct pcap_pkthdr header; , чтобы просто создать заголовок вместо указателя и получить его указатель с помощью amp;header

2. Здесь у вас есть рабочий пример, в котором используется pcap_next() : cpp.hotexamples.com/examples/-/-/pcap_next/…

3. @SamBob Я сделал это, но это не сработало. Программа по-прежнему аварийно завершает работу из-за ошибки сегментации.

Ответ №1:

Что касается состояния if(pcap_handle < 0) : документация по API здесь, https://www.tcpdump.org/manpages/pcap.3pcap.html, указывает, что функция « pcap_open_live() возвращает a pcap_t * при успешном выполнении и NULL при сбое. Проверка ошибок, вероятно, должна быть if(pcap_handle == NULL) в этом случае.

В текущей программе, если pcap_open_live() бы возвращалось NULL сообщение об ошибке, я ожидал бы, что строка packet = pcap_next(pcap_handle, header); либо выйдет из строя из-за того, что значение pcap_handle установлено в значение NULL, либо будет установлено header в NULL значение, либо оставит ее в неинициализированном состоянии, распознав pcap_handle значение NULL как ошибку.

В следующей строке header разыменовано, и если оно неинициализировано или NULL может привести к ошибке сегментации.

Итак, чтобы подвести итог, см. Комментарии, добавленные ниже:

 #include <pcap.h>
#include <stdlib.h>
#include <stdio.h>
#include "dump.h"

void pcap_fatal(const char *failed_in, const char *errbuf){
    printf("Fata Error in %s: %sn", failed_in, errbuf);
    exit(1);
}

int main(){
    struct pcap_pkthdr *header;
    const u_char *packet;
    char errbuf[PCAP_ERRBUF_SIZE];
    char *device;
    pcap_t *pcap_handle;
    int i;

    /*look for a device to capture packets*/
    device = pcap_lookupdev(errbuf);
    if(device == NULL)
        pcap_fatal("pcap_lookupdev", errbuf);
    printf("Sniffing on device %sn", device);

    /*opens the packet capturing device*/
    pcap_handle = pcap_open_live(device, 4096, 1, 0, errbuf); // << Fails and returns NULL.
    if(pcap_handle < 0){                                      // << Passes since value is NULL (typically zero).
        perror("ERROR: while opening pcap device");
        exit(1);
    }

    for(i=0;i<3;i  ){
        printf("n===packet %d===n", i 1);
        packet = pcap_next(pcap_handle, header);              // << Crashes due to pcap_handle being NULL, sets header to NULL or leaves uninitialized.
        printf("Got a %d bute packetn", header->len);        // << Dereferences pointer to nothing causing segmentation fault.
        dump(packet, header->len);
    }
    
    pcap_close(pcap_handle);
}
 

Я бы рекомендовал следующий код замены с несколькими дополнительными проверками для дополнительной защиты:

 #include <pcap.h>
#include <stdlib.h>
#include <stdio.h>
#include "dump.h"

void pcap_fatal(const char *failed_in, const char *errbuf){
    printf("Fata Error in %s: %sn", failed_in, errbuf);
    exit(1);
}

int main(){
    struct pcap_pkthdr *header;
    const u_char *packet;
    char errbuf[PCAP_ERRBUF_SIZE];
    char *device;
    pcap_t *pcap_handle;
    int i;

    /*look for a device to capture packets*/
    device = pcap_lookupdev(errbuf);
    if(device == NULL)
        pcap_fatal("pcap_lookupdev", errbuf);
    printf("Sniffing on device %sn", device);

    /*opens the packet capturing device*/
    pcap_handle = pcap_open_live(device, 4096, 1, 0, errbuf);
    if(pcap_handle == NULL){
        perror("ERROR: while opening pcap device");
        exit(1);
    }

    for(i=0;i<3;i  ){
        printf("n===packet %d===n", i 1);
        packet = pcap_next(pcap_handle, header);
        if((packet != NULL) amp;amp; (header != NULL)){
            printf("Got a %d bute packetn", header->len);
            dump(packet, header->len);
        }
    }
    
    pcap_close(pcap_handle);
}
 

Комментарии:

1. «В текущей программе, если pcap_open_live() бы возвращалось NULL сообщение об ошибке, я бы ожидал, что строка packet = pcap_next(pcap_handle, header); будет установлена header в значение NULL или же оставит ее в неинициализированном состоянии». Я бы ожидал pcap_next() , что попытаюсь использовать pcap_handle в качестве указателя и потерплю неудачу, пытаясь использовать элементы этой структуры.

2. «Проверка на ошибку, вероятно, должна быть if(pcap_handle == NULL) в этом случае». Да, именно так. if(pcap_handle < 0) почти наверняка не обнаружит ошибки — он подумает pcap_open_live() , что это удалось. Функции, возвращающие указатели, обычно возвращают NULL ошибки; функции, возвращающие целочисленное значение, часто возвращают отрицательные значения в качестве указаний на ошибку. pcap_open_live() возвращает указатель.

3. @user16139739 Я предполагаю, что pcap_next() проверка pcap_handle и header для NULL является частью его собственной проверки ошибок, но в документации это явно не указано. Однако, если pcap_next() не включает проверку на нуль, то да, это приведет к сбою. В любом случае, если он когда-либо доберется до строки printf("Got a %d bute packetn", header->len); , то он определенно потерпит неудачу из-за разыменования нулевого указателя.

4. «Я предполагаю, что pcap_next() проверяет pcap_handle и заголовок на NULL как часть собственной проверки ошибок, но в документации это явно не указано». В документации об этом не говорится, потому что это не так. libpcap предполагает, что автор кода выполняет правильную проверку ошибок при открытии устройства или файла записи. Если в документации ничего не говорится, не предполагайте, что это так.

5. @user16139739 На практике я обычно выполняю NULL проверки внутри любых библиотечных функций и генерирую код ошибки вместо того, чтобы разыменовывать указатель, но я могу согласиться, что это не всегда так. Я изменил ответ, чтобы охватить оба сценария. В общем, главное заключается в том, что программа выйдет из строя, если pcap_handle установлено значение NULL .

Ответ №2:

Как сказал Джонатан С., ваш тест на pcap_open_live() ошибочность.

Вы должны сделать

 /*opens the packet capturing device*/
pcap_handle = pcap_open_live(device, 4096, 1, 0, errbuf);
if(pcap_handle == NULL){
    fprintf(stderr, "ERROR: while opening pcap device %s: %sn", device, errbuf);
    exit(1);
}
 

потому что:

  1. pcap_open_live() возвращает нулевой указатель на ошибку — он не возвращает отрицательное число, потому что он не возвращает число;
  2. индикация ошибки не является значением errno, поэтому perror() не будет правильно сообщать об ошибке — errbuf содержит строку, описывающую ошибку, поэтому вы хотите распечатать ее, чтобы узнать, почему она не удалась;
  3. вы также должны сообщить имя устройства, так как это также может быть полезно для определения причины pcap_open_live() сбоя.

Обратите также внимание, что на большинстве платформ UN*X (Linux, *BSD, macOS и т. Д.) вам может потребоваться запустить программу с повышенными привилегиями, такими как привилегии root, чтобы открыть сетевой интерфейс для захвата, поэтому, если ваша программа сообщает об ошибке разрешения после того, как вы исправили ее, чтобы использовать приведенный выше код, попробуйте запустить ее от имени root.