libpcap:pcap_breakloop()导致内存泄漏 [英] libpcap: pcap_breakloop() causing memory leak

查看:418
本文介绍了libpcap:pcap_breakloop()导致内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在使用Linux pthreads libpcap 时,我注意到使用pcap_breakloop时有些奇怪的行为. 我的目标是: 打开一个新线程,该线程将运行pcap_loop并处理捕获的数据包,而主线程将执行其他操作.当收到信号(SIGINT)或退出时,将设置一个全局变量,并且主线程将发出pcap_breakloop来结束第二个线程,然后完成自身.

While working with Linux pthreads and libpcap I noticed some weird behavior when using pcap_breakloop. My goal is the following: Opening a new thread that will run pcap_loop and deal with captured packets, while the main thread will do other stuff. When the a signal (SIGINT) is received, or when it's time to exit, a global variable will be set and the main thread will issue pcap_breakloop to end the second thread and then finish itself.

这个想法可能仍然需要一些努力,但是我注意到的奇怪行为是:

The idea might still need some working, but the strange behavior I noticed was:

当我们创建一个bpf_program结构并使用pcap_compile进行编译时,要进一步将其用作捕获时的过滤器,一旦我们使用pcap_breakloop退出循环,就会有8个字节的内存泄漏(两个pcap_freecodepcap_close被调用).即使在不使用线程的情况下,这一点也是正确的.(在修改后的sniffex.c libpcap 示例中可以看到). 没有过滤器(没有pcap_compile调用或bpf_program)的相同代码退出而没有内存泄漏.

When we create a bpf_program structure and compile it with pcap_compile, to further use it as a filter on the capture, once we exit the loop using pcap_breakloop there is a memory leak of 8 bytes (both pcap_freecode and pcap_close being called). That is true even when not using threads (as can be seem on the modified sniffex.c libpcap example). The same code, without a filter (no pcap_compile call or bpf_program) exits without a memory leak.

像我在其他库(例如,gtk)上出现的某些内存泄漏一样,是从libmcap 发生的内存泄漏吗?图书馆以后会处理吗?还是我看不到我的代码有问题?

Is that memory leak something to be expected from libpcap like some memory leaks I've seem on other libraries (gtk for example)? Is it something the library will deal with later? Or is there something wrong on my code I can't see?

下面是代码示例:使用 pthreads 的代码示例和使用pcap_breakloop的修改后的 sniffex 示例(我还包括了原始中的diff sniffex 和经过修改的代码,因为代码很大,但是差别确实很小):

Below are the code examples: The one using pthreads and the modified sniffex example with pcap_breakloop (I also included a diff from the original sniffex and the modified one, since the code is quite large, but the difference is really small):

带有 pthreads 的示例:

Example with pthreads:

#include <pcap.h>
#include <stdio.h>
#include <pthread.h>
#include <signal.h>

int CLOSE_PROGRAM = 0;
int CAPTURE_SETUP_SUCCESS = 0;
pcap_t *handler;    //pcap_t handler and filter structure are globals so we can free them from other
struct bpf_program fp;  //functions

//Prototypes
void INT_Handler(int signum);   //signal handler
void capture_loop(unsigned char *args, const struct pcap_pkthdr *header, const unsigned char *packet);  //pcap_loop function
void *thread_entrypoint(void *data);    //the entry point for the second thread

void INT_Handler(int signum){
    printf("Interrupt Signal Received\n");
    CLOSE_PROGRAM = 1;
}

void capture_loop(unsigned char *args, const struct pcap_pkthdr *header, const unsigned char *packet){
    printf("Captured Packet\n");
    //Process packet
}

void *thread_entrypoint(void *data){
    //We can't cancel the thread before we free libpcap stuff
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

    char error[PCAP_ERRBUF_SIZE];

    char filter_exp[] = "ip";
    bpf_u_int32 mask;
    bpf_u_int32 net;

    char *dev = pcap_lookupdev(error);
    if(NULL == dev){
        printf("Failed to look up device!\n");
        return NULL;
    }
    if(pcap_lookupnet(dev, &net, &mask, error) == -1){
        printf("Failed to look up netmask: %s\n", error);
        net = 0;
        mask = 0;
    }

    printf("Opening live capture on device %s\n", dev);
    handler = pcap_open_live(dev, 1518, 0, 1000, error);
    if(NULL == handler){
        printf("Failed to open live capture!\n");
        return NULL;
    }

    if(pcap_compile(handler, &fp, filter_exp, 0, net?net:PCAP_NETMASK_UNKNOWN) == -1){
        printf("Failed to compiler filter!\n");
        return NULL;
    }
    if(pcap_setfilter(handler, &fp) == -1){
        printf("Failed to install filter!\n");
        return NULL;
    }

    CAPTURE_SETUP_SUCCESS = 1;
    pcap_loop(handler, 0, capture_loop, NULL);

    pcap_freecode(&fp);
    pcap_close(handler);

    printf("Capture end\n");
    return NULL;
}

int main(int argc, char **argv){
    pthread_t capture_thread;

    pthread_create(&capture_thread, NULL, &thread_entrypoint, NULL);

    signal(SIGINT, &INT_Handler);

    while(0 == CLOSE_PROGRAM){
        //Wait for SIGINT
    }

    if(1 == CAPTURE_SETUP_SUCCESS){
        pcap_breakloop(handler);
    }
    pthread_join(capture_thread, NULL);

    return 0;
}

修改的 sniffex 示例:

Modified sniffex example:

/*
 * sniffex.c
 *
 * Sniffer example of TCP/IP packet capture using libpcap.
 * 
 * Version 0.1.1 (2005-07-05)
 * Copyright (c) 2005 The Tcpdump Group
 *
 * This software is intended to be used as a practical example and 
 * demonstration of the libpcap library; available at:
 * http://www.tcpdump.org/
 *
 ****************************************************************************
 *
 * This software is a modification of Tim Carstens' "sniffer.c"
 * demonstration source code, released as follows:
 * 
 * sniffer.c
 * Copyright (c) 2002 Tim Carstens
 * 2002-01-07
 * Demonstration of using libpcap
 * timcarst -at- yahoo -dot- com
 * 
 * "sniffer.c" is distributed under these terms:
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 4. The name "Tim Carstens" may not be used to endorse or promote
 *    products derived from this software without prior written permission
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * <end of "sniffer.c" terms>
 *
 * This software, "sniffex.c", is a derivative work of "sniffer.c" and is
 * covered by the following terms:
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Because this is a derivative work, you must comply with the "sniffer.c"
 *    terms reproduced above.
 * 2. Redistributions of source code must retain the Tcpdump Group copyright
 *    notice at the top of this source file, this list of conditions and the
 *    following disclaimer.
 * 3. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 4. The names "tcpdump" or "libpcap" may not be used to endorse or promote
 *    products derived from this software without prior written permission.
 *
 * THERE IS ABSOLUTELY NO WARRANTY FOR THIS PROGRAM.
 * BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
 * FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
 * OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
 * PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
 * OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
 * TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
 * PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
 * REPAIR OR CORRECTION.
 * 
 * IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
 * WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
 * REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
 * INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
 * OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
 * TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
 * YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
 * PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 * <end of "sniffex.c" terms>
 * 
 ****************************************************************************
 *
 * Below is an excerpt from an email from Guy Harris on the tcpdump-workers
 * mail list when someone asked, "How do I get the length of the TCP
 * payload?" Guy Harris' slightly snipped response (edited by him to
 * speak of the IPv4 header length and TCP data offset without referring
 * to bitfield structure members) is reproduced below:
 * 
 * The Ethernet size is always 14 bytes.
 * 
 * <snip>...</snip>
 *
 * In fact, you *MUST* assume the Ethernet header is 14 bytes, *and*, if 
 * you're using structures, you must use structures where the members 
 * always have the same size on all platforms, because the sizes of the 
 * fields in Ethernet - and IP, and TCP, and... - headers are defined by 
 * the protocol specification, not by the way a particular platform's C 
 * compiler works.)
 *
 * The IP header size, in bytes, is the value of the IP header length,
 * as extracted from the "ip_vhl" field of "struct sniff_ip" with
 * the "IP_HL()" macro, times 4 ("times 4" because it's in units of
 * 4-byte words).  If that value is less than 20 - i.e., if the value
 * extracted with "IP_HL()" is less than 5 - you have a malformed
 * IP datagram.
 *
 * The TCP header size, in bytes, is the value of the TCP data offset,
 * as extracted from the "th_offx2" field of "struct sniff_tcp" with
 * the "TH_OFF()" macro, times 4 (for the same reason - 4-byte words).
 * If that value is less than 20 - i.e., if the value extracted with
 * "TH_OFF()" is less than 5 - you have a malformed TCP segment.
 *
 * So, to find the IP header in an Ethernet packet, look 14 bytes after 
 * the beginning of the packet data.  To find the TCP header, look 
 * "IP_HL(ip)*4" bytes after the beginning of the IP header.  To find the
 * TCP payload, look "TH_OFF(tcp)*4" bytes after the beginning of the TCP
 * header.
 * 
 * To find out how much payload there is:
 *
 * Take the IP *total* length field - "ip_len" in "struct sniff_ip" 
 * - and, first, check whether it's less than "IP_HL(ip)*4" (after
 * you've checked whether "IP_HL(ip)" is >= 5).  If it is, you have
 * a malformed IP datagram.
 *
 * Otherwise, subtract "IP_HL(ip)*4" from it; that gives you the length
 * of the TCP segment, including the TCP header.  If that's less than
 * "TH_OFF(tcp)*4" (after you've checked whether "TH_OFF(tcp)" is >= 5),
 * you have a malformed TCP segment.
 *
 * Otherwise, subtract "TH_OFF(tcp)*4" from it; that gives you the
 * length of the TCP payload.
 *
 * Note that you also need to make sure that you don't go past the end 
 * of the captured data in the packet - you might, for example, have a 
 * 15-byte Ethernet packet that claims to contain an IP datagram, but if 
 * it's 15 bytes, it has only one byte of Ethernet payload, which is too 
 * small for an IP header.  The length of the captured data is given in 
 * the "caplen" field in the "struct pcap_pkthdr"; it might be less than 
 * the length of the packet, if you're capturing with a snapshot length 
 * other than a value >= the maximum packet size.
 * <end of response>
 * 
 ****************************************************************************
 * 
 * Example compiler command-line for GCC:
 *   gcc -Wall -o sniffex sniffex.c -lpcap
 * 
 ****************************************************************************
 *
 * Code Comments
 *
 * This section contains additional information and explanations regarding
 * comments in the source code. It serves as documentaion and rationale
 * for why the code is written as it is without hindering readability, as it
 * might if it were placed along with the actual code inline. References in
 * the code appear as footnote notation (e.g. [1]).
 *
 * 1. Ethernet headers are always exactly 14 bytes, so we define this
 * explicitly with "#define". Since some compilers might pad structures to a
 * multiple of 4 bytes - some versions of GCC for ARM may do this -
 * "sizeof (struct sniff_ethernet)" isn't used.
 * 
 * 2. Check the link-layer type of the device that's being opened to make
 * sure it's Ethernet, since that's all we handle in this example. Other
 * link-layer types may have different length headers (see [1]).
 *
 * 3. This is the filter expression that tells libpcap which packets we're
 * interested in (i.e. which packets to capture). Since this source example
 * focuses on IP and TCP, we use the expression "ip", so we know we'll only
 * encounter IP packets. The capture filter syntax, along with some
 * examples, is documented in the tcpdump man page under "expression."
 * Below are a few simple examples:
 *
 * Expression           Description
 * ----------           -----------
 * ip                   Capture all IP packets.
 * tcp                  Capture only TCP packets.
 * tcp port 80          Capture only TCP packets with a port equal to 80.
 * ip host 10.1.2.3     Capture all IP packets to or from host 10.1.2.3.
 *
 ****************************************************************************
 *
 */

#define APP_NAME        "sniffex"
#define APP_DESC        "Sniffer example using libpcap"
#define APP_COPYRIGHT   "Copyright (c) 2005 The Tcpdump Group"
#define APP_DISCLAIMER  "THERE IS ABSOLUTELY NO WARRANTY FOR THIS PROGRAM."

#include <pcap.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

pcap_t *handle;             /* packet capture handle */

/* default snap length (maximum bytes per packet to capture) */
#define SNAP_LEN 1518

/* ethernet headers are always exactly 14 bytes [1] */
#define SIZE_ETHERNET 14

/* Ethernet addresses are 6 bytes */
#define ETHER_ADDR_LEN  6

/* Ethernet header */
struct sniff_ethernet {
        u_char  ether_dhost[ETHER_ADDR_LEN];    /* destination host address */
        u_char  ether_shost[ETHER_ADDR_LEN];    /* source host address */
        u_short ether_type;                     /* IP? ARP? RARP? etc */
};

/* IP header */
struct sniff_ip {
        u_char  ip_vhl;                 /* version << 4 | header length >> 2 */
        u_char  ip_tos;                 /* type of service */
        u_short ip_len;                 /* total length */
        u_short ip_id;                  /* identification */
        u_short ip_off;                 /* fragment offset field */
        #define IP_RF 0x8000            /* reserved fragment flag */
        #define IP_DF 0x4000            /* dont fragment flag */
        #define IP_MF 0x2000            /* more fragments flag */
        #define IP_OFFMASK 0x1fff       /* mask for fragmenting bits */
        u_char  ip_ttl;                 /* time to live */
        u_char  ip_p;                   /* protocol */
        u_short ip_sum;                 /* checksum */
        struct  in_addr ip_src,ip_dst;  /* source and dest address */
};
#define IP_HL(ip)               (((ip)->ip_vhl) & 0x0f)
#define IP_V(ip)                (((ip)->ip_vhl) >> 4)

/* TCP header */
typedef u_int tcp_seq;

struct sniff_tcp {
        u_short th_sport;               /* source port */
        u_short th_dport;               /* destination port */
        tcp_seq th_seq;                 /* sequence number */
        tcp_seq th_ack;                 /* acknowledgement number */
        u_char  th_offx2;               /* data offset, rsvd */
#define TH_OFF(th)      (((th)->th_offx2 & 0xf0) >> 4)
        u_char  th_flags;
        #define TH_FIN  0x01
        #define TH_SYN  0x02
        #define TH_RST  0x04
        #define TH_PUSH 0x08
        #define TH_ACK  0x10
        #define TH_URG  0x20
        #define TH_ECE  0x40
        #define TH_CWR  0x80
        #define TH_FLAGS        (TH_FIN|TH_SYN|TH_RST|TH_ACK|TH_URG|TH_ECE|TH_CWR)
        u_short th_win;                 /* window */
        u_short th_sum;                 /* checksum */
        u_short th_urp;                 /* urgent pointer */
};

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

void
print_payload(const u_char *payload, int len);

void
print_hex_ascii_line(const u_char *payload, int len, int offset);

void
print_app_banner(void);

void
print_app_usage(void);

/*
 * app name/banner
 */
void
print_app_banner(void)
{

    printf("%s - %s\n", APP_NAME, APP_DESC);
    printf("%s\n", APP_COPYRIGHT);
    printf("%s\n", APP_DISCLAIMER);
    printf("\n");

return;
}

/*
 * print help text
 */
void
print_app_usage(void)
{

    printf("Usage: %s [interface]\n", APP_NAME);
    printf("\n");
    printf("Options:\n");
    printf("    interface    Listen on <interface> for packets.\n");
    printf("\n");

return;
}

/*
 * print data in rows of 16 bytes: offset   hex   ascii
 *
 * 00000   47 45 54 20 2f 20 48 54  54 50 2f 31 2e 31 0d 0a   GET / HTTP/1.1..
 */
void
print_hex_ascii_line(const u_char *payload, int len, int offset)
{

    int i;
    int gap;
    const u_char *ch;

    /* offset */
    printf("%05d   ", offset);

    /* hex */
    ch = payload;
    for(i = 0; i < len; i++) {
        printf("%02x ", *ch);
        ch++;
        /* print extra space after 8th byte for visual aid */
        if (i == 7)
            printf(" ");
    }
    /* print space to handle line less than 8 bytes */
    if (len < 8)
        printf(" ");

    /* fill hex gap with spaces if not full line */
    if (len < 16) {
        gap = 16 - len;
        for (i = 0; i < gap; i++) {
            printf("   ");
        }
    }
    printf("   ");

    /* ascii (if printable) */
    ch = payload;
    for(i = 0; i < len; i++) {
        if (isprint(*ch))
            printf("%c", *ch);
        else
            printf(".");
        ch++;
    }

    printf("\n");

return;
}

/*
 * print packet payload data (avoid printing binary data)
 */
void
print_payload(const u_char *payload, int len)
{

    int len_rem = len;
    int line_width = 16;            /* number of bytes per line */
    int line_len;
    int offset = 0;                 /* zero-based offset counter */
    const u_char *ch = payload;

    if (len <= 0)
        return;

    /* data fits on one line */
    if (len <= line_width) {
        print_hex_ascii_line(ch, len, offset);
        return;
    }

    /* data spans multiple lines */
    for ( ;; ) {
        /* compute current line length */
        line_len = line_width % len_rem;
        /* print line */
        print_hex_ascii_line(ch, line_len, offset);
        /* compute total remaining */
        len_rem = len_rem - line_len;
        /* shift pointer to remaining bytes to print */
        ch = ch + line_len;
        /* add offset */
        offset = offset + line_width;
        /* check if we have line width chars or less */
        if (len_rem <= line_width) {
            /* print last line and get out */
            print_hex_ascii_line(ch, len_rem, offset);
            break;
        }
    }

return;
}

/*
 * dissect/print packet
 */
void
got_packet(u_char *args, const struct pcap_pkthdr *header, const u_char *packet)
{

    pcap_breakloop(handle);

    static int count = 1;                   /* packet counter */

    /* declare pointers to packet headers */
    const struct sniff_ethernet *ethernet;  /* The ethernet header [1] */
    const struct sniff_ip *ip;              /* The IP header */
    const struct sniff_tcp *tcp;            /* The TCP header */
    const char *payload;                    /* Packet payload */

    int size_ip;
    int size_tcp;
    int size_payload;

    printf("\nPacket number %d:\n", count);
    count++;

    /* define ethernet header */
    ethernet = (struct sniff_ethernet*)(packet);

    /* define/compute ip header offset */
    ip = (struct sniff_ip*)(packet + SIZE_ETHERNET);
    size_ip = IP_HL(ip)*4;
    if (size_ip < 20) {
        printf("   * Invalid IP header length: %u bytes\n", size_ip);
        return;
    }

    /* print source and destination IP addresses */
    printf("       From: %s\n", inet_ntoa(ip->ip_src));
    printf("         To: %s\n", inet_ntoa(ip->ip_dst));

    /* determine protocol */    
    switch(ip->ip_p) {
        case IPPROTO_TCP:
            printf("   Protocol: TCP\n");
            break;
        case IPPROTO_UDP:
            printf("   Protocol: UDP\n");
            return;
        case IPPROTO_ICMP:
            printf("   Protocol: ICMP\n");
            return;
        case IPPROTO_IP:
            printf("   Protocol: IP\n");
            return;
        default:
            printf("   Protocol: unknown\n");
            return;
    }

    /*
     *  OK, this packet is TCP.
     */

    /* define/compute tcp header offset */
    tcp = (struct sniff_tcp*)(packet + SIZE_ETHERNET + size_ip);
    size_tcp = TH_OFF(tcp)*4;
    if (size_tcp < 20) {
        printf("   * Invalid TCP header length: %u bytes\n", size_tcp);
        return;
    }

    printf("   Src port: %d\n", ntohs(tcp->th_sport));
    printf("   Dst port: %d\n", ntohs(tcp->th_dport));

    /* define/compute tcp payload (segment) offset */
    payload = (u_char *)(packet + SIZE_ETHERNET + size_ip + size_tcp);

    /* compute tcp payload (segment) size */
    size_payload = ntohs(ip->ip_len) - (size_ip + size_tcp);

    /*
     * Print payload data; it might be binary, so don't just
     * treat it as a string.
     */
    if (size_payload > 0) {
        printf("   Payload (%d bytes):\n", size_payload);
        print_payload(payload, size_payload);
    }

return;
}

int main(int argc, char **argv)
{

    char *dev = NULL;           /* capture device name */
    char errbuf[PCAP_ERRBUF_SIZE];      /* error buffer */

    char filter_exp[] = "ip";       /* filter expression [3] */
    struct bpf_program fp;          /* compiled filter program (expression) */
    bpf_u_int32 mask;           /* subnet mask */
    bpf_u_int32 net;            /* ip */
    int num_packets = 10;           /* number of packets to capture */

    print_app_banner();

    /* check for capture device name on command-line */
    if (argc == 2) {
        dev = argv[1];
    }
    else if (argc > 2) {
        fprintf(stderr, "error: unrecognized command-line options\n\n");
        print_app_usage();
        exit(EXIT_FAILURE);
    }
    else {
        /* find a capture device if not specified on command-line */
        dev = pcap_lookupdev(errbuf);
        if (dev == NULL) {
            fprintf(stderr, "Couldn't find default device: %s\n",
                errbuf);
            exit(EXIT_FAILURE);
        }
    }

    /* get network number and mask associated with capture device */
    if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) {
        fprintf(stderr, "Couldn't get netmask for device %s: %s\n",
            dev, errbuf);
        net = 0;
        mask = 0;
    }

    /* print capture info */
    printf("Device: %s\n", dev);
    printf("Number of packets: %d\n", num_packets);
    printf("Filter expression: %s\n", filter_exp);

    /* open capture device */
    handle = pcap_open_live(dev, SNAP_LEN, 1, 1000, errbuf);
    if (handle == NULL) {
        fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf);
        exit(EXIT_FAILURE);
    }

    /* make sure we're capturing on an Ethernet device [2] */
    if (pcap_datalink(handle) != DLT_EN10MB) {
        fprintf(stderr, "%s is not an Ethernet\n", dev);
        exit(EXIT_FAILURE);
    }

    /* compile the filter expression */
    if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) {
        fprintf(stderr, "Couldn't parse filter %s: %s\n",
            filter_exp, pcap_geterr(handle));
        exit(EXIT_FAILURE);
    }

    /* apply the compiled filter */
    if (pcap_setfilter(handle, &fp) == -1) {
        fprintf(stderr, "Couldn't install filter %s: %s\n",
            filter_exp, pcap_geterr(handle));
        exit(EXIT_FAILURE);
    }

    /* now we can set our callback function */
    pcap_loop(handle, num_packets, got_packet, NULL);

    /* cleanup */
    pcap_freecode(&fp);
    pcap_close(handle);

    printf("\nCapture complete.\n");

return 0;
}

原始 sniffex 和修改后的 sniffex 代码中的

diff:

diff from original sniffex and modified sniffex codes:

209a210,211
> pcap_t *handle;               /* packet capture handle */
> 
420a423,424
>   pcap_breakloop(handle);
> 
508d511
<   pcap_t *handle;             /* packet capture handle */

多线程代码的Valgrind消息:

==2734== 
==2734== HEAP SUMMARY:
==2734==     in use at exit: 8 bytes in 1 blocks
==2734==   total heap usage: 96 allocs, 95 frees, 82,123 bytes allocated
==2734== 
==2734== LEAK SUMMARY:
==2734==    definitely lost: 0 bytes in 0 blocks
==2734==    indirectly lost: 0 bytes in 0 blocks
==2734==      possibly lost: 0 bytes in 0 blocks
==2734==    still reachable: 8 bytes in 1 blocks
==2734==         suppressed: 0 bytes in 0 blocks
==2734== Rerun with --leak-check=full to see details of leaked memory
==2734== 
==2734== For counts of detected and suppressed errors, rerun with: -v
==2734== Use --track-origins=yes to see where uninitialised values come from
==2734== ERROR SUMMARY: 9 errors from 9 contexts (suppressed: 0 from 0)

Valgrind消息,用于修改的sniffex代码:

==2756== 
==2756== HEAP SUMMARY:
==2756==     in use at exit: 8 bytes in 1 blocks
==2756==   total heap usage: 97 allocs, 96 frees, 83,427 bytes allocated
==2756== 
==2756== LEAK SUMMARY:
==2756==    definitely lost: 0 bytes in 0 blocks
==2756==    indirectly lost: 0 bytes in 0 blocks
==2756==      possibly lost: 0 bytes in 0 blocks
==2756==    still reachable: 8 bytes in 1 blocks
==2756==         suppressed: 0 bytes in 0 blocks
==2756== Rerun with --leak-check=full to see details of leaked memory
==2756== 
==2756== For counts of detected and suppressed errors, rerun with: -v
==2756== Use --track-origins=yes to see where uninitialised values come from
==2756== ERROR SUMMARY: 9 errors from 9 contexts (suppressed: 0 from 0)

推荐答案

似乎这确实是libpcap上的错误.根据该GitHub页面上的错误报告,导致一些内存泄漏,非常类似于我在代码中遇到的泄漏.

It seems like this was indeed a bug on libpcap. According to this bug report on their GitHub page, pcap_compile was causing some memory leak very similar to the one I experienced on my codes.

==1505== still reachable: 8 bytes in 1 blocks

1个块中恰好有8个字节,就像我的valgrind报告中一样.有评论说最新的master分支版本已经修复了该错误.

Exactly 8 bytes in 1 blocks, like on my valgrind reports. There were comments saying the latest master branch version already fixed the bug.

当前,Ubuntu的存储库为libpcap 1.7.4-2,而GitHub上的最新版本显然为libpcap 1.8.1.我使用VM来测试代码,因为我需要以root用户身份运行valgrind才能使libpcap实时捕获正常工作.我清除了当前安装的libpcap库,克隆了最新版本,从源代码构建它,并将其符号链接为/usr/lib上的旧版本,以便在具有最新版本的VM上再次对其进行测试.

Currently, Ubuntu's repositories have libpcap 1.7.4-2, while the latest release on GitHub is libpcap 1.8.1 apparently. I was using a VM to test the codes, since I needed to run valgrind as root to get the libpcap live capture working. I purged the currently installed libpcap library, cloned the latest version, built it from source and symlinked it as the older version on /usr/lib to test it again on the VM with the latest release.

运行相同的代码(多线程示例和修改后的sniffex代码)导致报告的内存泄漏绝对为

Running the same code (both the multithreaded example and the modified sniffex code) resulted in a report with absolutely zero memory leaks!

它显然与flex有关,而不与pcap本身有关,但是如果有人遇到相同的神秘内存泄漏,并为它感到困扰,请下载最新的pcap版本,并且应该对其进行修复.

It apparently was related to flex and not pcap itself, but in case anyone runs into the same mysterious memory leak, and is bothered by having it, download the latest pcap version and it should be fixed.

感谢@Kamiccolo指出它可能是错误的可能性! :)

Thanks for @Kamiccolo for pointing the possibility of it being a bug! :)

这篇关于libpcap:pcap_breakloop()导致内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆