/* Copyright (C) 2008 The Android Open Source Project
**
** This software is licensed under the terms of the GNU General Public
** License version 2, as published by the Free Software Foundation, and
** may be copied, distributed, and modified under those terms.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
*/
#include "tcpdump.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

int  qemu_tcpdump_active;

static FILE*     capture_file;
static uint64_t  capture_count;
static uint64_t  capture_size;
static int       capture_init;

static void
capture_atexit(void)
{
    if (qemu_tcpdump_active) {
        fclose(capture_file);
        qemu_tcpdump_active = 0;
    }
}

/* See http://wiki.wireshark.org/Development/LibpcapFileFormat for
 * the complete description of the packet capture file format
 */

#define  PCAP_MAGIC     0xa1b2c3d4
#define  PCAP_MAJOR     2
#define  PCAP_MINOR     4
#define  PCAP_SNAPLEN   65535
#define  PCAP_ETHERNET  1

static int
pcap_write_header( FILE*  out )
{
    typedef struct {
        uint32_t   magic;
        uint16_t   version_major;
        uint16_t   version_minor;
        int32_t    this_zone;
        uint32_t   sigfigs;
        uint32_t   snaplen;
        uint32_t   network;
    } PcapHeader;

    PcapHeader  h;

    h.magic         = PCAP_MAGIC;
    h.version_major = PCAP_MAJOR;
    h.version_minor = PCAP_MINOR;
    h.this_zone     = 0;
    h.sigfigs       = 0;  /* all tools set it to 0 in practice */
    h.snaplen       = PCAP_SNAPLEN;
    h.network       = PCAP_ETHERNET;

    if (fwrite(&h, sizeof(h), 1, out) != 1) {
        return -1;
    }
    return 0;
}

int
qemu_tcpdump_start( const char*  filepath )
{
    if (!capture_init) {
        capture_init = 1;
        atexit(capture_atexit);
    }

    qemu_tcpdump_stop();

    if (filepath == NULL)
        return -1;

    capture_file = fopen(filepath, "wb");
    if (capture_file == NULL)
        return -1;

    if (pcap_write_header(capture_file) < 0)
        return -1;

    qemu_tcpdump_active = 1;
    return 0;
}

void
qemu_tcpdump_stop( void )
{
    if (!qemu_tcpdump_active)
        return;

    qemu_tcpdump_active = 0;

    capture_count = 0;
    capture_size  = 0;

    fclose(capture_file);
    capture_file = NULL;
}

void
qemu_tcpdump_packet( const void*  base, int  len )
{
    typedef struct {
        uint32_t  ts_sec;
        uint32_t  ts_usec;
        uint32_t  incl_len;
        uint32_t  orig_len;
    } PacketHeader;

    PacketHeader    h;
    struct timeval  now;
    int             len2 = len;

    if (len2 > PCAP_SNAPLEN)
        len2 = PCAP_SNAPLEN;

    gettimeofday(&now, NULL);
    h.ts_sec   = (uint32_t) now.tv_sec;
    h.ts_usec  = (uint32_t) now.tv_usec;
    h.incl_len = (uint32_t) len2;
    h.orig_len = (uint32_t) len;

    fwrite( &h, sizeof(h), 1, capture_file );
    fwrite( base, 1, len2, capture_file );

    capture_count += 1;
    capture_size  += len2;
}

void
qemu_tcpdump_stats( uint64_t  *pcount, uint64_t*  psize )
{
    *pcount = capture_count;
    *psize  = capture_size;
}