/* -*- 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 <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <check.h>
#include "common.h"

char filenameprefix[] = "lldp_send";

static struct lldpd test_lldpd = { .g_config = {
				       .c_cap_advertise =
					   1, /* Chassis capabilities advertisement */
				       .c_mgmt_advertise =
					   1, /* Management addresses advertisement */
				   } };

#define ck_assert_str_eq_n(X, Y, N) \
  ck_assert_msg(!strncmp(X, Y, N),  \
      "Assertion '" #X "==" #Y "' failed: " #X "==\"%s\", " #Y "==\"%s\"", X, Y)

static void
check_received_port(struct lldpd_port *sport, struct lldpd_port *rport)
{
	ck_assert_int_eq(rport->p_id_subtype, sport->p_id_subtype);
	ck_assert_int_eq(rport->p_id_len, sport->p_id_len);
	ck_assert_str_eq_n(rport->p_id, sport->p_id, sport->p_id_len);
	ck_assert_str_eq(rport->p_descr, sport->p_descr);
#ifdef ENABLE_DOT3
	ck_assert_int_eq(rport->p_mfs, sport->p_mfs);
#endif
}

static void
check_received_chassis(struct lldpd_chassis *schassis, struct lldpd_chassis *rchassis)
{
	ck_assert_int_eq(rchassis->c_id_subtype, schassis->c_id_subtype);
	ck_assert_int_eq(rchassis->c_id_len, schassis->c_id_len);
	ck_assert_str_eq_n(rchassis->c_id, schassis->c_id, schassis->c_id_len);
	ck_assert_str_eq(rchassis->c_name, schassis->c_name);
	ck_assert_str_eq(rchassis->c_descr, schassis->c_descr);
	ck_assert_int_eq(rchassis->c_cap_available, schassis->c_cap_available);
	ck_assert_int_eq(rchassis->c_cap_enabled, schassis->c_cap_enabled);
}

#ifdef ENABLE_LLDPMED
static void
check_received_port_med(struct lldpd_port *sport, struct lldpd_port *rport)
{
	ck_assert_int_eq(rport->p_med_cap_enabled, sport->p_med_cap_enabled);
	ck_assert_int_eq(rport->p_med_cap_enabled, sport->p_med_cap_enabled);
	ck_assert_int_eq(rport->p_med_location[LLDP_MED_LOCFORMAT_CIVIC - 1].format,
	    sport->p_med_location[LLDP_MED_LOCFORMAT_CIVIC - 1].format);
	ck_assert_int_eq(rport->p_med_location[LLDP_MED_LOCFORMAT_CIVIC - 1].data_len,
	    sport->p_med_location[LLDP_MED_LOCFORMAT_CIVIC - 1].data_len);
	ck_assert_str_eq_n(rport->p_med_location[LLDP_MED_LOCFORMAT_CIVIC - 1].data,
	    sport->p_med_location[LLDP_MED_LOCFORMAT_CIVIC - 1].data,
	    sport->p_med_location[LLDP_MED_LOCFORMAT_CIVIC - 1].data_len);
	ck_assert_int_eq(rport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].type,
	    sport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].type);
	ck_assert_int_eq(
	    rport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].tagged,
	    sport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].tagged);
	ck_assert_int_eq(rport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].vid,
	    sport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].vid);
	ck_assert_int_eq(
	    rport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].priority,
	    sport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].priority);
	ck_assert_int_eq(rport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].dscp,
	    sport->p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].dscp);
	ck_assert_int_eq(rport->p_med_power.devicetype, sport->p_med_power.devicetype);
	ck_assert_int_eq(rport->p_med_power.source, sport->p_med_power.source);
	ck_assert_int_eq(rport->p_med_power.priority, sport->p_med_power.priority);
	ck_assert_int_eq(rport->p_med_power.val, sport->p_med_power.val);
}

static void
check_received_chassis_med(struct lldpd_chassis *schassis,
    struct lldpd_chassis *rchassis)
{
	ck_assert_int_eq(rchassis->c_med_cap_available, schassis->c_med_cap_available);
	ck_assert_int_eq(rchassis->c_med_type, schassis->c_med_type);
	ck_assert_str_eq(rchassis->c_med_hw, schassis->c_med_hw);
	ck_assert_str_eq(rchassis->c_med_fw, schassis->c_med_fw);
	ck_assert_str_eq(rchassis->c_med_sw, schassis->c_med_sw);
	ck_assert_str_eq(rchassis->c_med_sn, schassis->c_med_sn);
}
#endif

#ifdef ENABLE_DOT3
static void
check_received_port_dot3(struct lldpd_port *sport, struct lldpd_port *rport)
{
	ck_assert_int_eq(rport->p_aggregid, sport->p_aggregid);
	ck_assert_int_eq(rport->p_macphy.autoneg_support,
	    sport->p_macphy.autoneg_support);
	ck_assert_int_eq(rport->p_macphy.autoneg_enabled,
	    sport->p_macphy.autoneg_enabled);
	ck_assert_int_eq(rport->p_macphy.autoneg_advertised,
	    sport->p_macphy.autoneg_advertised);
	ck_assert_int_eq(rport->p_macphy.mau_type, sport->p_macphy.mau_type);
}
#endif

START_TEST(test_send_rcv_basic)
{
	int n;
	struct packet *pkt;
	struct lldpd_chassis *nchassis = NULL;
	struct lldpd_port *nport = NULL;

	/* Populate port and chassis */
	hardware.h_lport.p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
	hardware.h_lport.p_id = "FastEthernet 1/5";
	hardware.h_lport.p_id_len = strlen(hardware.h_lport.p_id);
	hardware.h_lport.p_descr = "Fake port description";
	hardware.h_lport.p_mfs = 1516;
	chassis.c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
	chassis.c_id = macaddress;
	chassis.c_id_len = ETHER_ADDR_LEN;
	chassis.c_name = "First chassis";
	chassis.c_descr = "Chassis description";
	chassis.c_cap_available = chassis.c_cap_enabled = LLDP_CAP_ROUTER;

	/* Build packet */
	n = lldp_send(&test_lldpd, &hardware);
	if (n != 0) {
		fail("unable to build packet");
		return;
	}
	if (TAILQ_EMPTY(&pkts)) {
		fail("no packets sent");
		return;
	}
	pkt = TAILQ_FIRST(&pkts);
	fail_unless(TAILQ_NEXT(pkt, next) == NULL, "more than one packet sent");

	/* decode the retrieved packet calling lldp_decode() */
	fail_unless(lldp_decode(NULL, pkt->data, pkt->size, &hardware, &nchassis,
			&nport) != -1);
	if (!nchassis || !nport) {
		fail("unable to decode packet");
		return;
	}
	/* verify port values */
	check_received_port(&hardware.h_lport, nport);
	/* verify chassis values */
	check_received_chassis(&chassis, nchassis);
}
END_TEST

#define ETHERTYPE_OFFSET 2 * ETHER_ADDR_LEN
#define VLAN_TAG_SIZE 2
START_TEST(test_send_rcv_vlan_tx)
{
	int n;
	struct packet *pkt;
	struct lldpd_chassis *nchassis = NULL;
	struct lldpd_port *nport = NULL;
	int vlan_id = 100;
	int vlan_prio = 5;
	int vlan_dei = 1;
	unsigned int vlan_tag = 0;
	unsigned int tmp;

	/* Populate port and chassis */
	hardware.h_lport.p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
	hardware.h_lport.p_id = "FastEthernet 1/5";
	hardware.h_lport.p_id_len = strlen(hardware.h_lport.p_id);
	hardware.h_lport.p_descr = "Fake port description";
	hardware.h_lport.p_mfs = 1516;

	/* Assembly VLAN tag: Priority(3bits) | DEI(1bit) | VID(12bits) */
	vlan_tag =
	    ((vlan_prio & 0x7) << 13) | ((vlan_dei & 0x1) << 12) | (vlan_id & 0xfff);
	hardware.h_lport.p_vlan_tx_tag = vlan_tag;
	hardware.h_lport.p_vlan_tx_enabled = 1;
	chassis.c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
	chassis.c_id = macaddress;
	chassis.c_id_len = ETHER_ADDR_LEN;
	chassis.c_name = "First chassis";
	chassis.c_descr = "Chassis description";
	chassis.c_cap_available = chassis.c_cap_enabled = LLDP_CAP_ROUTER;

	/* Build packet */
	n = lldp_send(&test_lldpd, &hardware);
	if (n != 0) {
		fail("unable to build packet");
		return;
	}
	if (TAILQ_EMPTY(&pkts)) {
		fail("no packets sent");
		return;
	}
	pkt = TAILQ_FIRST(&pkts);
	fail_unless(TAILQ_NEXT(pkt, next) == NULL, "more than one packet sent");

	/* Check ETHER_TYPE, should be VLAN */
	memcpy(&tmp, (unsigned char *)pkt->data + ETHERTYPE_OFFSET, ETHER_TYPE_LEN);
	ck_assert_uint_eq(ntohl(tmp) >> 16, ETHERTYPE_VLAN);

	/* Check VLAN tag */
	memcpy(&tmp, (unsigned char *)pkt->data + ETHERTYPE_OFFSET + ETHER_TYPE_LEN,
	    VLAN_TAG_SIZE);
	ck_assert_uint_eq(ntohl(tmp) >> 16, vlan_tag);

	/* Remove VLAN ethertype and VLAN tag */
	memmove((unsigned char *)pkt->data + ETHERTYPE_OFFSET,
	    /* move all after VLAN tag */
	    (unsigned char *)pkt->data + ETHERTYPE_OFFSET + ETHER_TYPE_LEN +
		VLAN_TAG_SIZE,
	    /* size without src and dst MAC, VLAN tag */
	    pkt->size - (ETHERTYPE_OFFSET + ETHER_TYPE_LEN + VLAN_TAG_SIZE));

	/* Decode the packet without VLAN tag, calling lldp_decode() */
	fail_unless(lldp_decode(NULL, pkt->data, pkt->size, &hardware, &nchassis,
			&nport) != -1);
	if (!nchassis || !nport) {
		fail("unable to decode packet");
		return;
	}

	/* Verify port values (VLAN information is not checked here) */
	check_received_port(&hardware.h_lport, nport);
	/* Verify chassis values */
	check_received_chassis(&chassis, nchassis);
}
END_TEST

#ifdef ENABLE_DOT1
/* This test case tests send and receive of all DOT1 TLVs(2005 and 2009):
   Port Valn ID, VLAN, Port Protocol VLAN ID, Protocol Identity,
   VID Usage Digest, Management VID, and 802.1ax Link Aggregation TLVs */
START_TEST(test_send_rcv_dot1_tlvs)
{
	int n;
	struct lldpd_vlan *rvlan, vlan1, vlan2, vlan3;
	struct lldpd_ppvid ppvid, *rppvid;
	struct lldpd_pi pi1, pi2, *rpi;
	struct lldpd_chassis *nchassis = NULL;
	struct lldpd_port *nport = NULL;
	struct packet *pkt;

	/* Populate port and chassis */
	hardware.h_lport.p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
	hardware.h_lport.p_id = macaddress;
	hardware.h_lport.p_id_len = ETHER_ADDR_LEN;
	hardware.h_lport.p_descr = "Fake port description";
	hardware.h_lport.p_mfs = 1516;
	hardware.h_lport.p_pvid = 1500;
	chassis.c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL;
	chassis.c_id = "Chassis name";
	chassis.c_id_len = strlen(chassis.c_id);
	chassis.c_name = "Second chassis";
	chassis.c_descr = "Chassis description";
	chassis.c_cap_available = LLDP_CAP_ROUTER | LLDP_CAP_BRIDGE;
	chassis.c_cap_enabled = LLDP_CAP_ROUTER;
	vlan1.v_name = "Voice";
	vlan1.v_vid = 157;
	vlan2.v_name = "Data";
	vlan2.v_vid = 1247;
	vlan3.v_name = "Control";
	vlan3.v_vid = 741;
	TAILQ_INSERT_TAIL(&hardware.h_lport.p_vlans, &vlan1, v_entries);
	TAILQ_INSERT_TAIL(&hardware.h_lport.p_vlans, &vlan2, v_entries);
	TAILQ_INSERT_TAIL(&hardware.h_lport.p_vlans, &vlan3, v_entries);
	ppvid.p_cap_status = 3;
	ppvid.p_ppvid = 1500;
	TAILQ_INSERT_TAIL(&hardware.h_lport.p_ppvids, &ppvid, p_entries);
	pi1.p_pi = "IEEE Link Aggregration Control Protocol 802.3ad";
	pi1.p_pi_len = strlen(pi1.p_pi);
	pi2.p_pi = "IEEE Link Layer Discovery Protocol 802.1ab-2005";
	pi2.p_pi_len = strlen(pi2.p_pi);
	TAILQ_INSERT_TAIL(&hardware.h_lport.p_pids, &pi1, p_entries);
	TAILQ_INSERT_TAIL(&hardware.h_lport.p_pids, &pi2, p_entries);

	/* Build packet */
	n = lldp_send(&test_lldpd, &hardware);
	if (n != 0) {
		fail("unable to build packet");
		return;
	}
	if (TAILQ_EMPTY(&pkts)) {
		fail("no packets sent");
		return;
	}
	pkt = TAILQ_FIRST(&pkts);
	fail_unless(TAILQ_NEXT(pkt, next) == NULL, "more than one packet sent");

	/* decode the retrieved packet calling lldp_decode() */
	fail_unless(lldp_decode(NULL, pkt->data, pkt->size, &hardware, &nchassis,
			&nport) != -1);
	if (!nchassis || !nport) {
		fail("unable to decode packet");
		return;
	}

	/* verify port values */
	check_received_port(&hardware.h_lport, nport);
	/* verify chassis values */
	check_received_chassis(&chassis, nchassis);

	if (TAILQ_EMPTY(&nport->p_vlans)) {
		fail("no VLAN");
		return;
	}

	rvlan = TAILQ_FIRST(&nport->p_vlans);
	ck_assert_int_eq(rvlan->v_vid, vlan1.v_vid);
	ck_assert_str_eq(rvlan->v_name, vlan1.v_name);

	rvlan = TAILQ_NEXT(rvlan, v_entries);
	if (!rvlan) {
		fail("no more VLAN");
		return;
	}
	ck_assert_int_eq(rvlan->v_vid, vlan2.v_vid);
	ck_assert_str_eq(rvlan->v_name, vlan2.v_name);

	rvlan = TAILQ_NEXT(rvlan, v_entries);
	if (!rvlan) {
		fail("no more VLAN");
		return;
	}
	ck_assert_int_eq(rvlan->v_vid, vlan3.v_vid);
	ck_assert_str_eq(rvlan->v_name, vlan3.v_name);

	rvlan = TAILQ_NEXT(rvlan, v_entries);
	fail_unless(rvlan == NULL);

	ck_assert_int_eq(nport->p_pvid, hardware.h_lport.p_pvid);

	if (TAILQ_EMPTY(&nport->p_ppvids)) {
		fail("no Port Protocal VLAN ID");
		return;
	}
	rppvid = TAILQ_FIRST(&nport->p_ppvids);
	ck_assert_int_eq(rppvid->p_cap_status, ppvid.p_cap_status);
	ck_assert_int_eq(rppvid->p_ppvid, ppvid.p_ppvid);

	if (TAILQ_EMPTY(&nport->p_pids)) {
		fail("no Protocal Identity TLV");
		return;
	}
	rpi = TAILQ_FIRST(&nport->p_pids);
	ck_assert_int_eq(rpi->p_pi_len, pi1.p_pi_len);
	ck_assert_str_eq_n(rpi->p_pi, pi1.p_pi, pi1.p_pi_len);

	rpi = TAILQ_NEXT(rpi, p_entries);
	if (!rpi) {
		fail("no more Protocol Identity TLVs");
		return;
	}
	ck_assert_int_eq(rpi->p_pi_len, pi2.p_pi_len);
	ck_assert_str_eq_n(rpi->p_pi, pi2.p_pi, pi2.p_pi_len);

	rpi = TAILQ_NEXT(rpi, p_entries);
	fail_unless(rpi == NULL);
}
END_TEST
#endif

#ifdef ENABLE_LLDPMED
START_TEST(test_send_rcv_med)
{
	int n;
	struct packet *pkt;
	struct lldpd_chassis *nchassis = NULL;
	struct lldpd_port *nport = NULL;

	/* Populate port and chassis */
	hardware.h_lport.p_id_subtype = LLDP_PORTID_SUBTYPE_LLADDR;
	hardware.h_lport.p_id = macaddress;
	hardware.h_lport.p_id_len = ETHER_ADDR_LEN;
	hardware.h_lport.p_descr = "Fake port description";
	hardware.h_lport.p_mfs = 1516;
	chassis.c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL;
	chassis.c_id = "Chassis name";
	chassis.c_id_len = strlen(chassis.c_id);
	chassis.c_name = "Third chassis";
	chassis.c_descr = "Chassis description";
	chassis.c_cap_available = LLDP_CAP_ROUTER | LLDP_CAP_BRIDGE;
	chassis.c_cap_enabled = LLDP_CAP_ROUTER;
	chassis.c_med_cap_available = LLDP_MED_CAP_CAP | LLDP_MED_CAP_POLICY |
	    LLDP_MED_CAP_LOCATION | LLDP_MED_CAP_MDI_PSE | LLDP_MED_CAP_IV;
	chassis.c_med_type = LLDP_MED_CLASS_III;
	chassis.c_med_hw = "hardware rev 5";
	chassis.c_med_fw = "47b5";
	chassis.c_med_sw = "2.6.22b5";
	chassis.c_med_sn = "SN 47842";
	hardware.h_lport.p_med_cap_enabled = chassis.c_med_cap_available;
	hardware.h_lport.p_med_location[LLDP_MED_LOCFORMAT_CIVIC - 1].format =
	    LLDP_MED_LOCFORMAT_CIVIC;
	hardware.h_lport.p_med_location[LLDP_MED_LOCFORMAT_CIVIC - 1].data =
	    "Your favorite city";
	hardware.h_lport.p_med_location[LLDP_MED_LOCFORMAT_CIVIC - 1].data_len =
	    sizeof("Your favorite city");
	hardware.h_lport.p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].type =
	    LLDP_MED_APPTYPE_SOFTPHONEVOICE;
	hardware.h_lport.p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].tagged = 1;
	hardware.h_lport.p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].vid = 51;
	hardware.h_lport.p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].priority = 6;
	hardware.h_lport.p_med_policy[LLDP_MED_APPTYPE_SOFTPHONEVOICE - 1].dscp = 46;
	hardware.h_lport.p_med_power.devicetype = LLDP_MED_POW_TYPE_PSE;
	hardware.h_lport.p_med_power.source = LLDP_MED_POW_SOURCE_PRIMARY;
	hardware.h_lport.p_med_power.priority = LLDP_MED_POW_PRIO_HIGH;
	hardware.h_lport.p_med_power.val = 65;

	/* Build packet */
	n = lldp_send(&test_lldpd, &hardware);
	if (n != 0) {
		fail("unable to build packet");
		return;
	}
	if (TAILQ_EMPTY(&pkts)) {
		fail("no packets sent");
		return;
	}
	pkt = TAILQ_FIRST(&pkts);
	fail_unless(TAILQ_NEXT(pkt, next) == NULL, "more than one packet sent");

	/* decode the retrieved packet calling lldp_decode() */
	fail_unless(lldp_decode(NULL, pkt->data, pkt->size, &hardware, &nchassis,
			&nport) != -1);
	if (!nchassis || !nport) {
		fail("unable to decode packet");
		return;
	}
	/* verify port values */
	check_received_port(&hardware.h_lport, nport);
	/* verify chassis values */
	check_received_chassis(&chassis, nchassis);

	/* veridfy med content */
	check_received_port_med(&hardware.h_lport, nport);
	check_received_chassis_med(&chassis, nchassis);
}
END_TEST
#endif

#ifdef ENABLE_DOT3
START_TEST(test_send_rcv_dot3)
{
	int n;
	struct lldpd_chassis *nchassis = NULL;
	struct lldpd_port *nport = NULL;
	struct packet *pkt;

	/* Populate port and chassis */
	hardware.h_lport.p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME;
	hardware.h_lport.p_id = "FastEthernet 1/5";
	hardware.h_lport.p_id_len = strlen(hardware.h_lport.p_id);
	hardware.h_lport.p_descr = "Fake port description";
	hardware.h_lport.p_mfs = 1516;
	hardware.h_lport.p_aggregid = 5;
	hardware.h_lport.p_macphy.autoneg_support = 1;
	hardware.h_lport.p_macphy.autoneg_enabled = 1;
	hardware.h_lport.p_macphy.autoneg_advertised = LLDP_DOT3_LINK_AUTONEG_10BASE_T |
	    LLDP_DOT3_LINK_AUTONEG_10BASET_FD | LLDP_DOT3_LINK_AUTONEG_100BASE_TX |
	    LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD;
	hardware.h_lport.p_macphy.mau_type = LLDP_DOT3_MAU_100BASETXFD;
	chassis.c_id_subtype = LLDP_CHASSISID_SUBTYPE_LLADDR;
	chassis.c_id = macaddress;
	chassis.c_id_len = ETHER_ADDR_LEN;
	chassis.c_name = "Fourth chassis";
	chassis.c_descr = "Long chassis description";
	chassis.c_cap_available = chassis.c_cap_enabled =
	    LLDP_CAP_ROUTER | LLDP_CAP_WLAN;

	/* Build packet */
	n = lldp_send(&test_lldpd, &hardware);
	if (n != 0) {
		fail("unable to build packet");
		return;
	}
	if (TAILQ_EMPTY(&pkts)) {
		fail("no packets sent");
		return;
	}
	pkt = TAILQ_FIRST(&pkts);
	fail_unless(TAILQ_NEXT(pkt, next) == NULL, "more than one packet sent");

	/* decode the retrieved packet calling lldp_decode() */
	fail_unless(lldp_decode(NULL, pkt->data, pkt->size, &hardware, &nchassis,
			&nport) != -1);
	if (!nchassis || !nport) {
		fail("unable to decode packet");
		return;
	}
	/* verify port values */
	check_received_port(&hardware.h_lport, nport);
	/* verify chassis values */
	check_received_chassis(&chassis, nchassis);
	/* verify dot3 values */
	check_received_port_dot3(&hardware.h_lport, nport);
}
END_TEST
#endif

START_TEST(test_recv_min)
{
	char pkt1[] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, 0x00, 0x17, 0xd1, 0xa8,
		0x35, 0xbe, 0x88, 0xcc, 0x02, 0x07, 0x04, 0x00, 0x17, 0xd1, 0xa8, 0x35,
		0xbf, 0x04, 0x07, 0x03, 0x00, 0x17, 0xd1, 0xa8, 0x36, 0x02, 0x06, 0x02,
		0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
		0x00, 0x00 };
	/* This is:
Ethernet II, Src: Nortel_a8:35:be (00:17:d1:a8:35:be), Dst: LLDP_Multicast
(01:80:c2:00:00:0e) Destination: LLDP_Multicast (01:80:c2:00:00:0e) Source:
Nortel_a8:35:be (00:17:d1:a8:35:be) Type: 802.1 Link Layer Discovery Protocol (LLDP)
(0x88cc) Link Layer Discovery Protocol Chassis Subtype = MAC address 0000 001. .... ....
= TLV Type: Chassis Id (1)
	.... ...0 0000 0111 = TLV Length: 7
	Chassis Id Subtype: MAC address (4)
	Chassis Id: Nortel_a8:35:bf (00:17:d1:a8:35:bf)
    Port Subtype = MAC address
	0000 010. .... .... = TLV Type: Port Id (2)
	.... ...0 0000 0111 = TLV Length: 7
	Port Id Subtype: MAC address (3)
	Port Id: Nortel_a8:36:02 (00:17:d1:a8:36:02)
    Time To Live = 120 sec
	0000 011. .... .... = TLV Type: Time to Live (3)
	.... ...0 0000 0010 = TLV Length: 2
	Seconds: 120
    End of LLDPDU
	0000 000. .... .... = TLV Type: End of LLDPDU (0)
	.... ...0 0000 0000 = TLV Length: 0
	*/
	struct lldpd_chassis *nchassis = NULL;
	struct lldpd_port *nport = NULL;
	char mac1[] = { 0x0, 0x17, 0xd1, 0xa8, 0x35, 0xbf };
	char mac2[] = { 0x0, 0x17, 0xd1, 0xa8, 0x36, 0x02 };

	fail_unless(
	    lldp_decode(NULL, pkt1, sizeof(pkt1), &hardware, &nchassis, &nport) != -1);
	if (!nchassis || !nport) {
		fail("unable to decode packet");
		return;
	}
	ck_assert_int_eq(nchassis->c_id_subtype, LLDP_CHASSISID_SUBTYPE_LLADDR);
	ck_assert_int_eq(nchassis->c_id_len, ETHER_ADDR_LEN);
	fail_unless(memcmp(mac1, nchassis->c_id, ETHER_ADDR_LEN) == 0);
	ck_assert_int_eq(nport->p_id_subtype, LLDP_PORTID_SUBTYPE_LLADDR);
	ck_assert_int_eq(nport->p_id_len, ETHER_ADDR_LEN);
	fail_unless(memcmp(mac2, nport->p_id, ETHER_ADDR_LEN) == 0);
	ck_assert_ptr_eq(nchassis->c_name, NULL);
	ck_assert_ptr_eq(nchassis->c_descr, NULL);
	ck_assert_ptr_eq(nport->p_descr, NULL);
	ck_assert_int_eq(nport->p_ttl, 120);
}
END_TEST

START_TEST(test_recv_lldpd)
{
	/* This is a frame generated by lldpd */
	char pkt1[] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e, 0x00, 0x16, 0x17, 0x2f,
		0xa1, 0xb6, 0x88, 0xcc, 0x02, 0x07, 0x04, 0x00, 0x16, 0x17, 0x2f, 0xa1,
		0xb6, 0x04, 0x07, 0x03, 0x00, 0x16, 0x17, 0x2f, 0xa1, 0xb6, 0x06, 0x02,
		0x00, 0x78, 0x0a, 0x1a, 0x6e, 0x61, 0x72, 0x75, 0x74, 0x6f, 0x2e, 0x58,
		0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
		0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x0c, 0x3f, 0x4c, 0x69, 0x6e, 0x75,
		0x78, 0x20, 0x32, 0x2e, 0x36, 0x2e, 0x32, 0x39, 0x2d, 0x32, 0x2d, 0x61,
		0x6d, 0x64, 0x36, 0x34, 0x20, 0x23, 0x31, 0x20, 0x53, 0x4d, 0x50, 0x20,
		0x53, 0x75, 0x6e, 0x20, 0x4d, 0x61, 0x79, 0x20, 0x31, 0x37, 0x20, 0x31,
		0x37, 0x3a, 0x31, 0x35, 0x3a, 0x34, 0x37, 0x20, 0x55, 0x54, 0x43, 0x20,
		0x32, 0x30, 0x30, 0x39, 0x20, 0x78, 0x38, 0x36, 0x5f, 0x36, 0x34, 0x0e,
		0x04, 0x00, 0x1c, 0x00, 0x14, 0x10, 0x0c, 0x05, 0x01, 0x0a, 0xee, 0x50,
		0x4b, 0x02, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08, 0x04, 0x65, 0x74, 0x68,
		0x30, 0xfe, 0x09, 0x00, 0x12, 0x0f, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00,
		0xfe, 0x09, 0x00, 0x12, 0x0f, 0x01, 0x03, 0x6c, 0x03, 0x00, 0x10, 0xfe,
		0x06, 0x00, 0x12, 0x0f, 0x04, 0x05, 0xdc, 0xfe, 0x07, 0x00, 0x12, 0xbb,
		0x01, 0x00, 0x00, 0x00, 0xfe, 0x0f, 0x00, 0x12, 0xbb, 0x05, 0x4e, 0x44,
		0x39, 0x39, 0x31, 0x37, 0x38, 0x39, 0x37, 0x30, 0x32, 0xfe, 0x0b, 0x00,
		0x12, 0xbb, 0x06, 0x30, 0x38, 0x30, 0x30, 0x31, 0x32, 0x20, 0xfe, 0x12,
		0x00, 0x12, 0xbb, 0x07, 0x32, 0x2e, 0x36, 0x2e, 0x32, 0x39, 0x2d, 0x32,
		0x2d, 0x61, 0x6d, 0x64, 0x36, 0x34, 0xfe, 0x10, 0x00, 0x12, 0xbb, 0x08,
		0x31, 0x30, 0x35, 0x38, 0x32, 0x30, 0x38, 0x35, 0x30, 0x30, 0x30, 0x39,
		0xfe, 0x15, 0x00, 0x12, 0xbb, 0x09, 0x4e, 0x45, 0x43, 0x20, 0x43, 0x6f,
		0x6d, 0x70, 0x75, 0x74, 0x65, 0x72, 0x73, 0x20, 0x53, 0x41, 0x53, 0xfe,
		0x13, 0x00, 0x12, 0xbb, 0x0a, 0x50, 0x4f, 0x57, 0x45, 0x52, 0x4d, 0x41,
		0x54, 0x45, 0x20, 0x56, 0x4c, 0x33, 0x35, 0x30, 0xfe, 0x0d, 0x00, 0x12,
		0xbb, 0x0b, 0x31, 0x30, 0x30, 0x32, 0x30, 0x37, 0x31, 0x32, 0x30, 0x00,
		0x00 };
	/* This is:
Ethernet II, Src: Msi_2f:a1:b6 (00:16:17:2f:a1:b6), Dst: LLDP_Multicast
(01:80:c2:00:00:0e) Destination: LLDP_Multicast (01:80:c2:00:00:0e) Source: Msi_2f:a1:b6
(00:16:17:2f:a1:b6) Type: 802.1 Link Layer Discovery Protocol (LLDP) (0x88cc) Link Layer
Discovery Protocol Chassis Subtype = MAC address 0000 001. .... .... = TLV Type: Chassis
Id (1)
	.... ...0 0000 0111 = TLV Length: 7
	Chassis Id Subtype: MAC address (4)
	Chassis Id: Msi_2f:a1:b6 (00:16:17:2f:a1:b6)
    Port Subtype = MAC address
	0000 010. .... .... = TLV Type: Port Id (2)
	.... ...0 0000 0111 = TLV Length: 7
	Port Id Subtype: MAC address (3)
	Port Id: Msi_2f:a1:b6 (00:16:17:2f:a1:b6)
    Time To Live = 120 sec
	0000 011. .... .... = TLV Type: Time to Live (3)
	.... ...0 0000 0010 = TLV Length: 2
	Seconds: 120
    System Name = naruto.XXXXXXXXXXXXXXXXXXX
	0000 101. .... .... = TLV Type: System Name (5)
	.... ...0 0001 1010 = TLV Length: 26
	System Name = naruto.bureau.b1.p.fti.net
    System Description = Linux 2.6.29-2-amd64 #1 SMP Sun May 17 17:15:47 UTC 2009 x86_64
	0000 110. .... .... = TLV Type: System Description (6)
	.... ...0 0011 1111 = TLV Length: 63
	System Description = Linux 2.6.29-2-amd64 #1 SMP Sun May 17 17:15:47 UTC 2009
x86_64 Capabilities 0000 111. .... .... = TLV Type: System Capabilities (7)
	.... ...0 0000 0100 = TLV Length: 4
	Capabilities: 0x001c
	    .... .... .... .1.. = Bridge
	    .... .... .... 1... = WLAN access point
	    .... .... ...1 .... = Router
	Enabled Capabilities: 0x0014
	    .... .... .... .1.. = Bridge
	    .... .... ...1 .... = Router
    Management Address
	0001 000. .... .... = TLV Type: Management Address (8)
	.... ...0 0000 1100 = TLV Length: 12
	Address String Length: 5
	Address Subtype: IPv4 (1)
	Management Address: 10.238.80.75
	Interface Subtype: ifIndex (2)
	Interface Number: 3
	OID String Length: 0
    Port Description = eth0
	0000 100. .... .... = TLV Type: Port Description (4)
	.... ...0 0000 0100 = TLV Length: 4
	Port Description: eth0
    IEEE 802.3 - Link Aggregation
	1111 111. .... .... = TLV Type: Organization Specific (127)
	.... ...0 0000 1001 = TLV Length: 9
	Organization Unique Code: IEEE 802.3 (0x00120f)
	IEEE 802.3 Subtype: Link Aggregation (0x03)
	Aggregation Status: 0x01
	    .... ...1 = Aggregation Capability: Yes
	    .... ..0. = Aggregation Status: Not Enabled
	Aggregated Port Id: 0
    IEEE 802.3 - MAC/PHY Configuration/Status
	1111 111. .... .... = TLV Type: Organization Specific (127)
	.... ...0 0000 1001 = TLV Length: 9
	Organization Unique Code: IEEE 802.3 (0x00120f)
	IEEE 802.3 Subtype: MAC/PHY Configuration/Status (0x01)
	Auto-Negotiation Support/Status: 0x03
	    .... ...1 = Auto-Negotiation: Supported
	    .... ..1. = Auto-Negotiation: Enabled
	PMD Auto-Negotiation Advertised Capability: 0x6C03
	    .... .... .... ...1 = 1000BASE-T (full duplex mode)
	    .... .... .... ..1. = 1000BASE-T (half duplex mode)
	    .... .1.. .... .... = 100BASE-TX (full duplex mode)
	    .... 1... .... .... = 100BASE-TX (half duplex mode)
	    ..1. .... .... .... = 10BASE-T (full duplex mode)
	    .1.. .... .... .... = 10BASE-T (half duplex mode)
	Operational MAU Type: 100BaseTXFD - 2 pair category 5 UTP, full duplex mode
(0x0010) IEEE 802.3 - Maximum Frame Size 1111 111. .... .... = TLV Type: Organization
Specific (127)
	.... ...0 0000 0110 = TLV Length: 6
	Organization Unique Code: IEEE 802.3 (0x00120f)
	IEEE 802.3 Subtype: Maximum Frame Size (0x04)
	Maximum Frame Size: 1500
    TIA - Media Capabilities
	1111 111. .... .... = TLV Type: Organization Specific (127)
	.... ...0 0000 0111 = TLV Length: 7
	Organization Unique Code: TIA (0x0012bb)
	Media Subtype: Media Capabilities (0x01)
	Capabilities: 0x0000
	Class Type: Type Not Defined
    TIA - Inventory - Hardware Revision
	1111 111. .... .... = TLV Type: Organization Specific (127)
	.... ...0 0000 1111 = TLV Length: 15
	Organization Unique Code: TIA (0x0012bb)
	Media Subtype: Inventory - Hardware Revision (0x05)
	Hardware Revision: ND991789702
    TIA - Inventory - Firmware Revision
	1111 111. .... .... = TLV Type: Organization Specific (127)
	.... ...0 0000 1011 = TLV Length: 10
	Organization Unique Code: TIA (0x0012bb)
	Media Subtype: Inventory - Firmware Revision (0x06)
	Firmware Revision: 080012
    TIA - Inventory - Software Revision
	1111 111. .... .... = TLV Type: Organization Specific (127)
	.... ...0 0001 0010 = TLV Length: 18
	Organization Unique Code: TIA (0x0012bb)
	Media Subtype: Inventory - Software Revision (0x07)
	Software Revision: 2.6.29-2-amd64
    TIA - Inventory - Serial Number
	1111 111. .... .... = TLV Type: Organization Specific (127)
	.... ...0 0001 0000 = TLV Length: 16
	Organization Unique Code: TIA (0x0012bb)
	Media Subtype: Inventory - Serial Number (0x08)
	Serial Number: 105820850009
    TIA - Inventory - Manufacturer Name
	1111 111. .... .... = TLV Type: Organization Specific (127)
	.... ...0 0001 0101 = TLV Length: 21
	Organization Unique Code: TIA (0x0012bb)
	Media Subtype: Inventory - Manufacturer Name (0x09)
	Manufacturer Name: NEC Computers SAS
    TIA - Inventory - Model Name
	1111 111. .... .... = TLV Type: Organization Specific (127)
	.... ...0 0001 0011 = TLV Length: 19
	Organization Unique Code: TIA (0x0012bb)
	Media Subtype: Inventory - Model Name (0x0a)
	Model Name: POWERMATE VL350
    TIA - Inventory - Asset ID
	1111 111. .... .... = TLV Type: Organization Specific (127)
	.... ...0 0000 1101 = TLV Length: 13
	Organization Unique Code: TIA (0x0012bb)
	Media Subtype: Inventory - Asset ID (0x0b)
	Asset ID: 100207120
    End of LLDPDU
	0000 000. .... .... = TLV Type: End of LLDPDU (0)
	.... ...0 0000 0000 = TLV Length: 0
	*/
	struct lldpd_chassis *nchassis = NULL;
	struct lldpd_port *nport = NULL;
	char mac1[] = { 0x00, 0x16, 0x17, 0x2f, 0xa1, 0xb6 };

	fail_unless(
	    lldp_decode(NULL, pkt1, sizeof(pkt1), &hardware, &nchassis, &nport) != -1);
	if (!nchassis || !nport) {
		fail("unable to decode packet");
		return;
	}
	ck_assert_int_eq(nchassis->c_id_subtype, LLDP_CHASSISID_SUBTYPE_LLADDR);
	ck_assert_int_eq(nchassis->c_id_len, ETHER_ADDR_LEN);
	fail_unless(memcmp(mac1, nchassis->c_id, ETHER_ADDR_LEN) == 0);
	ck_assert_int_eq(nport->p_id_subtype, LLDP_PORTID_SUBTYPE_LLADDR);
	ck_assert_int_eq(nport->p_id_len, ETHER_ADDR_LEN);
	fail_unless(memcmp(mac1, nport->p_id, ETHER_ADDR_LEN) == 0);
	ck_assert_int_eq(nport->p_ttl, 120);
	ck_assert_str_eq(nchassis->c_name, "naruto.XXXXXXXXXXXXXXXXXXX");
	ck_assert_str_eq(nchassis->c_descr,
	    "Linux 2.6.29-2-amd64 #1 SMP Sun May 17 17:15:47 UTC 2009 x86_64");
	ck_assert_str_eq(nport->p_descr, "eth0");
	ck_assert_int_eq(nchassis->c_cap_available,
	    LLDP_CAP_WLAN | LLDP_CAP_ROUTER | LLDP_CAP_BRIDGE);
	ck_assert_int_eq(nchassis->c_cap_enabled, LLDP_CAP_ROUTER | LLDP_CAP_BRIDGE);
	ck_assert_int_eq(nchassis->c_mgmt.tqh_first->m_addr.inet.s_addr,
	    (u_int32_t)inet_addr("10.238.80.75"));
	ck_assert_int_eq(nchassis->c_mgmt.tqh_first->m_iface, 3);
#ifdef ENABLE_DOT3
	ck_assert_int_eq(nport->p_aggregid, 0);
	ck_assert_int_eq(nport->p_macphy.autoneg_enabled, 1);
	ck_assert_int_eq(nport->p_macphy.autoneg_support, 1);
	ck_assert_int_eq(nport->p_macphy.autoneg_advertised,
	    LLDP_DOT3_LINK_AUTONEG_1000BASE_TFD | LLDP_DOT3_LINK_AUTONEG_1000BASE_T |
		LLDP_DOT3_LINK_AUTONEG_100BASE_TXFD |
		LLDP_DOT3_LINK_AUTONEG_100BASE_TX | LLDP_DOT3_LINK_AUTONEG_10BASET_FD |
		LLDP_DOT3_LINK_AUTONEG_10BASE_T);
	ck_assert_int_eq(nport->p_macphy.mau_type, LLDP_DOT3_MAU_100BASETXFD);
	ck_assert_int_eq(nport->p_mfs, 1500);
#endif
#ifdef ENABLE_LLDPMED
	ck_assert_int_eq(nchassis->c_med_type, 0);
	ck_assert_str_eq(nchassis->c_med_hw, "ND991789702");
	ck_assert_str_eq(nchassis->c_med_fw, "080012 "); /* Extra space */
	ck_assert_str_eq(nchassis->c_med_sw, "2.6.29-2-amd64");
	ck_assert_str_eq(nchassis->c_med_sn, "105820850009");
	ck_assert_str_eq(nchassis->c_med_manuf, "NEC Computers SAS");
	ck_assert_str_eq(nchassis->c_med_model, "POWERMATE VL350");
	ck_assert_str_eq(nchassis->c_med_asset, "100207120");
#endif
}
END_TEST

static Suite *
lldp_suite(void)
{
	Suite *s = suite_create("LLDP");
	TCase *tc_send = tcase_create("Send LLDP packets");
	TCase *tc_receive = tcase_create("Receive LLDP packets");

	/* Send tests are first run without knowing the result. The
	   result is then checked with:
	     tshark -V -T text -r tests/lldp_send_0000.pcap

	   If the result is correct, then, we get the packet as C
	   bytes using wireshark export to C arrays (tshark seems not
	   be able to do this).
	*/

	tcase_add_checked_fixture(tc_send, pcap_setup, pcap_teardown);
	tcase_add_test(tc_send, test_send_rcv_basic);
	tcase_add_test(tc_send, test_send_rcv_vlan_tx);
#ifdef ENABLE_DOT1
	tcase_add_test(tc_send, test_send_rcv_dot1_tlvs);
#endif
#ifdef ENABLE_LLDPMED
	tcase_add_test(tc_send, test_send_rcv_med);
#endif
#ifdef ENABLE_DOT3
	tcase_add_test(tc_send, test_send_rcv_dot3);
#endif
	suite_add_tcase(s, tc_send);

	tcase_add_test(tc_receive, test_recv_min);
	tcase_add_test(tc_receive, test_recv_lldpd);
	suite_add_tcase(s, tc_receive);

	return s;
}

int
main()
{
	int number_failed;
	Suite *s = lldp_suite();
	SRunner *sr = srunner_create(s);
	srunner_set_fork_status(sr, CK_NOFORK); /* Can't fork because
						   we need to write
						   files */
	srunner_run_all(sr, CK_ENV);
	number_failed = srunner_ntests_failed(sr);
	srunner_free(sr);
	return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
