/** @file
 @brief LLDP definitions and handler

 This is not to be included by the application.
 */

/*
 * Copyright (c) 2017 Intel Corporation
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#ifndef ZEPHYR_INCLUDE_NET_LLDP_H_
#define ZEPHYR_INCLUDE_NET_LLDP_H_

/**
 * @brief LLDP definitions and helpers
 * @defgroup lldp Link Layer Discovery Protocol definitions and helpers
 * @ingroup networking
 * @{
 */

#ifdef __cplusplus
extern "C" {
#endif

/** @cond INTERNAL_HIDDEN */

#define LLDP_TLV_GET_LENGTH(type_length)	(type_length & BIT_MASK(9))
#define LLDP_TLV_GET_TYPE(type_length)		((uint8_t)(type_length >> 9))

/* LLDP Definitions */

/* According to the spec, End of LLDPDU TLV value is constant. */
#define NET_LLDP_END_LLDPDU_VALUE 0x0000

/*
 * For the Chassis ID TLV Value, if subtype is a MAC address then we must
 * use values from CONFIG_NET_LLDP_CHASSIS_ID_MAC0 through
 * CONFIG_NET_LLDP_CHASSIS_ID_MAC5. If not, we use CONFIG_NET_LLDP_CHASSIS_ID.
 *
 * FIXME: implement a similar scheme for subtype 5 (network address).
 */
#if defined(CONFIG_NET_LLDP_CHASSIS_ID_SUBTYPE)
#if (CONFIG_NET_LLDP_CHASSIS_ID_SUBTYPE == 4)
#define NET_LLDP_CHASSIS_ID_VALUE		\
	{					\
	  CONFIG_NET_LLDP_CHASSIS_ID_MAC0,	\
	  CONFIG_NET_LLDP_CHASSIS_ID_MAC1,	\
	  CONFIG_NET_LLDP_CHASSIS_ID_MAC2,	\
	  CONFIG_NET_LLDP_CHASSIS_ID_MAC3,	\
	  CONFIG_NET_LLDP_CHASSIS_ID_MAC4,	\
	  CONFIG_NET_LLDP_CHASSIS_ID_MAC5 	\
	}

#define NET_LLDP_CHASSIS_ID_VALUE_LEN (6)
#else
#define NET_LLDP_CHASSIS_ID_VALUE CONFIG_NET_LLDP_CHASSIS_ID
#define NET_LLDP_CHASSIS_ID_VALUE_LEN (sizeof(CONFIG_NET_LLDP_CHASSIS_ID) - 1)
#endif
#else
#define NET_LLDP_CHASSIS_ID_VALUE 0
#define NET_LLDP_CHASSIS_ID_VALUE_LEN 0
#endif

/*
 * For the Port ID TLV Value, if subtype is a MAC address then we must
 * use values from CONFIG_NET_LLDP_PORT_ID_MAC0 through
 * CONFIG_NET_LLDP_PORT_ID_MAC5. If not, we use CONFIG_NET_LLDP_PORT_ID.
 *
 * FIXME: implement a similar scheme for subtype 4 (network address).
 */
#if defined(CONFIG_NET_LLDP_PORT_ID_SUBTYPE)
#if (CONFIG_NET_LLDP_PORT_ID_SUBTYPE == 3)
#define NET_LLDP_PORT_ID_VALUE		\
	{				\
	  CONFIG_NET_LLDP_PORT_ID_MAC0,	\
	  CONFIG_NET_LLDP_PORT_ID_MAC1, \
	  CONFIG_NET_LLDP_PORT_ID_MAC2, \
	  CONFIG_NET_LLDP_PORT_ID_MAC3, \
	  CONFIG_NET_LLDP_PORT_ID_MAC4, \
	  CONFIG_NET_LLDP_PORT_ID_MAC5  \
	}

#define NET_LLDP_PORT_ID_VALUE_LEN (6)
#else
#define NET_LLDP_PORT_ID_VALUE CONFIG_NET_LLDP_PORT_ID
#define NET_LLDP_PORT_ID_VALUE_LEN (sizeof(CONFIG_NET_LLDP_PORT_ID) - 1)
#endif
#else
#define NET_LLDP_PORT_ID_VALUE 0
#define NET_LLDP_PORT_ID_VALUE_LEN 0
#endif

/*
 * TLVs Length.
 * Note that TLVs that have a subtype must have a byte added to their length.
 */
#define NET_LLDP_CHASSIS_ID_TLV_LEN (NET_LLDP_CHASSIS_ID_VALUE_LEN + 1)
#define NET_LLDP_PORT_ID_TLV_LEN (NET_LLDP_PORT_ID_VALUE_LEN + 1)
#define NET_LLDP_TTL_TLV_LEN (2)

/*
 * Time to Live value.
 * Calculate based on section 9.2.5.22 from LLDP spec.
 *
 * FIXME: when the network interface is about to be ‘disabled’ TTL shall be set
 * to zero so LLDP Rx agents can invalidate the entry related to this node.
 */
#if defined(CONFIG_NET_LLDP_TX_INTERVAL) && defined(CONFIG_NET_LLDP_TX_HOLD)
#define NET_LLDP_TTL \
	MIN((CONFIG_NET_LLDP_TX_INTERVAL * CONFIG_NET_LLDP_TX_HOLD) + 1, 65535)
#endif


struct net_if;

/** @endcond */

/** TLV Types. Please refer to table 8-1 from IEEE 802.1AB standard. */
enum net_lldp_tlv_type {
	LLDP_TLV_END_LLDPDU          = 0, /**< End Of LLDPDU (optional)      */
	LLDP_TLV_CHASSIS_ID          = 1, /**< Chassis ID (mandatory)        */
	LLDP_TLV_PORT_ID             = 2, /**< Port ID (mandatory)           */
	LLDP_TLV_TTL                 = 3, /**< Time To Live (mandatory)      */
	LLDP_TLV_PORT_DESC           = 4, /**< Port Description (optional)   */
	LLDP_TLV_SYSTEM_NAME         = 5, /**< System Name (optional)        */
	LLDP_TLV_SYSTEM_DESC         = 6, /**< System Description (optional) */
	LLDP_TLV_SYSTEM_CAPABILITIES = 7, /**< System Capability (optional)  */
	LLDP_TLV_MANAGEMENT_ADDR     = 8, /**< Management Address (optional) */
	/* Types 9 - 126 are reserved. */
	LLDP_TLV_ORG_SPECIFIC       = 127, /**< Org specific TLVs (optional) */
};

/** Chassis ID TLV, see chapter 8.5.2 in IEEE 802.1AB */
struct net_lldp_chassis_tlv {
	/** 7 bits for type, 9 bits for length */
	uint16_t type_length;
	/** ID subtype */
	uint8_t subtype;
	/** Chassis ID value */
	uint8_t value[NET_LLDP_CHASSIS_ID_VALUE_LEN];
} __packed;

/** Port ID TLV, see chapter 8.5.3 in IEEE 802.1AB */
struct net_lldp_port_tlv {
	/** 7 bits for type, 9 bits for length */
	uint16_t type_length;
	/** ID subtype */
	uint8_t subtype;
	/** Port ID value */
	uint8_t value[NET_LLDP_PORT_ID_VALUE_LEN];
} __packed;

/** Time To Live TLV, see chapter 8.5.4 in IEEE 802.1AB */
struct net_lldp_time_to_live_tlv {
	/** 7 bits for type, 9 bits for length */
	uint16_t type_length;
	/** Time To Live (TTL) value */
	uint16_t ttl;
} __packed;

/**
 * LLDP Data Unit (LLDPDU) shall contain the following ordered TLVs
 * as stated in "8.2 LLDPDU format" from the IEEE 802.1AB
 */
struct net_lldpdu {
	struct net_lldp_chassis_tlv chassis_id;	/**< Mandatory Chassis TLV */
	struct net_lldp_port_tlv port_id;	/**< Mandatory Port TLV */
	struct net_lldp_time_to_live_tlv ttl;	/**< Mandatory TTL TLV */
} __packed;

/**
 * @brief Set the LLDP data unit for a network interface.
 *
 * @param iface Network interface
 * @param lldpdu LLDP data unit struct
 *
 * @return 0 if ok, <0 if error
 */
int net_lldp_config(struct net_if *iface, const struct net_lldpdu *lldpdu);

/**
 * @brief Set the Optional LLDP TLVs for a network interface.
 *
 * @param iface Network interface
 * @param tlv LLDP optional TLVs following mandatory part
 * @param len Length of the optional TLVs
 *
 * @return 0 if ok, <0 if error
 */
int net_lldp_config_optional(struct net_if *iface, const uint8_t *tlv,
			     size_t len);

/**
 * @brief Initialize LLDP engine.
 */
void net_lldp_init(void);

/**
 * @brief LLDP Receive packet callback
 *
 * Callback gets called upon receiving packet. It is responsible for
 * freeing packet or indicating to the stack that it needs to free packet
 * by returning correct net_verdict.
 *
 * Returns:
 *  - NET_DROP, if packet was invalid, rejected or we want the stack to free it.
 *    In this case the core stack will free the packet.
 *  - NET_OK, if the packet was accepted, in this case the ownership of the
 *    net_pkt goes to callback and core network stack will forget it.
 */
typedef enum net_verdict (*net_lldp_recv_cb_t)(struct net_if *iface,
					       struct net_pkt *pkt);

/**
 * @brief Register LLDP Rx callback function
 *
 * @param iface Network interface
 * @param cb Callback function
 *
 * @return 0 if ok, < 0 if error
 */
int net_lldp_register_callback(struct net_if *iface, net_lldp_recv_cb_t cb);

/**
 * @brief Parse LLDP packet
 *
 * @param iface Network interface
 * @param pkt Network packet
 *
 * @return Return the policy for network buffer
 */
enum net_verdict net_lldp_recv(struct net_if *iface, struct net_pkt *pkt);

/**
 * @brief Set LLDP protocol data unit (LLDPDU) for the network interface.
 *
 * @param iface Network interface
 *
 * @return <0 if error, index in lldp array if iface is found there
 */
#if defined(CONFIG_NET_LLDP)
int net_lldp_set_lldpdu(struct net_if *iface);
#else
#define net_lldp_set_lldpdu(iface)
#endif

/**
 * @brief Unset LLDP protocol data unit (LLDPDU) for the network interface.
 *
 * @param iface Network interface
 */
#if defined(CONFIG_NET_LLDP)
void net_lldp_unset_lldpdu(struct net_if *iface);
#else
#define net_lldp_unset_lldpdu(iface)
#endif

#ifdef __cplusplus
}
#endif

/**
 * @}
 */

#endif /* ZEPHYR_INCLUDE_NET_LLDP_H_ */
