/* -*- mode: c; c-file-style: "openbsd" -*- */
/*
 * Copyright (c) 2015 Vincent Bernat <bernat@luffy.cx>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <fcntl.h>
#include <check.h>
#include "common.h"

int dump = -1;
char *filename = NULL;
struct pkts_t pkts;
char macaddress[ETHER_ADDR_LEN] = { 0x5e, 0x10, 0x8e, 0xe7, 0x84, 0xad };
struct lldpd_hardware hardware;
struct lldpd_chassis chassis;

int
pcap_send(struct lldpd *cfg, struct lldpd_hardware *hardware, char *buffer, size_t size)
{
	struct pcaprec_hdr hdr;
	struct packet *pkt;
	int n;

	/* Write pcap record header */
	hdr.ts_sec = time(NULL);
	hdr.ts_usec = 0;
	hdr.incl_len = hdr.orig_len = size;
	n = write(dump, &hdr, sizeof(hdr));
	if (n == 1) {
		fail("unable to write pcap record header to %s", filename);
		return -1;
	}

	/* Write data */
	n = write(dump, buffer, size);
	if (n == -1) {
		fail("unable to write pcap data to %s", filename);
		return -1;
	}

	/* Append to list of packets */
	pkt =
	    (struct packet *)malloc(size + sizeof(TAILQ_HEAD(, packet)) + sizeof(int));
	if (!pkt) {
		fail("unable to allocate packet");
		return -1;
	}
	memcpy(pkt->data, buffer, size);
	pkt->size = size;
	TAILQ_INSERT_TAIL(&pkts, pkt, next);
	return 0;
}

struct lldpd_ops pcap_ops = {
	.send = pcap_send,
	.recv = NULL,	 /* Won't be used */
	.cleanup = NULL, /* Won't be used */
};

void
pcap_setup(void)
{
	static int serial = 0;
	struct pcap_hdr hdr;
	int n;
	/* Prepare packet buffer */
	TAILQ_INIT(&pkts);
	/* Open a new dump file */
	n = asprintf(&filename, "%s_%04d.pcap", filenameprefix, serial++);
	if (n == -1) {
		fail("unable to compute filename");
		return;
	}
	dump = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0644);
	if (dump == -1) {
		fail("unable to open %s", filename);
		return;
	}
	/* Write a PCAP header */
	hdr.magic_number = 0xa1b2c3d4;
	hdr.version_major = 2;
	hdr.version_minor = 4;
	hdr.thiszone = 0;
	hdr.sigfigs = 0;
	hdr.snaplen = 65535;
	hdr.network = 1;
	n = write(dump, &hdr, sizeof(hdr));
	if (n == -1) {
		fail("unable to write pcap header to %s", filename);
		return;
	}
	/* Prepare hardware */
	memset(&hardware, 0, sizeof(struct lldpd_hardware));
	TAILQ_INIT(&hardware.h_rports);
#ifdef ENABLE_DOT1
	TAILQ_INIT(&hardware.h_lport.p_vlans);
	TAILQ_INIT(&hardware.h_lport.p_ppvids);
	TAILQ_INIT(&hardware.h_lport.p_pids);
#endif
	hardware.h_mtu = 1500;
	hardware.h_ifindex = 4;
	strlcpy(hardware.h_ifname, "test", sizeof(hardware.h_ifname));
	memcpy(hardware.h_lladdr, macaddress, ETHER_ADDR_LEN);
	hardware.h_ops = &pcap_ops;
	/* Prepare chassis */
	memset(&chassis, 0, sizeof(struct lldpd_chassis));
	hardware.h_lport.p_chassis = &chassis;
}

void
pcap_teardown()
{
	struct packet *npkt, *pkt;
	for (pkt = TAILQ_FIRST(&pkts); pkt != NULL; pkt = npkt) {
		npkt = TAILQ_NEXT(pkt, next);
		TAILQ_REMOVE(&pkts, pkt, next);
		free(pkt);
	}
	if (dump != -1) {
		close(dump);
		dump = -1;
	}
	if (filename) {
		free(filename);
		filename = NULL;
	}
}

/* Disable leak detection sanitizer */
int __lsan_is_turned_off(void);
int
__lsan_is_turned_off(void)
{
	return 1;
}
