#ifndef _NFT_BRIDGE_H_
#define _NFT_BRIDGE_H_

#include <netinet/in.h>
//#include <linux/netfilter_bridge/ebtables.h>
#include <linux/netfilter/x_tables.h>
#include <linux/netfilter/nf_tables.h>
#include <net/ethernet.h>
#include <libiptc/libxtc.h>

/* We use replace->flags, so we can't use the following values:
 * 0x01 == OPT_COMMAND, 0x02 == OPT_TABLE, 0x100 == OPT_ZERO */
#define LIST_N	  0x04
#define LIST_C	  0x08
#define LIST_X	  0x10
#define LIST_MAC2 0x20

extern unsigned char eb_mac_type_unicast[ETH_ALEN];
extern unsigned char eb_msk_type_unicast[ETH_ALEN];
extern unsigned char eb_mac_type_multicast[ETH_ALEN];
extern unsigned char eb_msk_type_multicast[ETH_ALEN];
extern unsigned char eb_mac_type_broadcast[ETH_ALEN];
extern unsigned char eb_msk_type_broadcast[ETH_ALEN];
extern unsigned char eb_mac_type_bridge_group[ETH_ALEN];
extern unsigned char eb_msk_type_bridge_group[ETH_ALEN];

int ebt_get_mac_and_mask(const char *from, unsigned char *to, unsigned char *mask);

/* From: include/linux/netfilter_bridge/ebtables.h
 *
 * Adapted for the need of the ebtables-compat.
 */

#define EBT_TABLE_MAXNAMELEN 32
#define EBT_FUNCTION_MAXNAMELEN EBT_TABLE_MAXNAMELEN

/* verdicts >0 are "branches" */
#define EBT_ACCEPT   -1
#define EBT_DROP     -2
#define EBT_CONTINUE -3
#define EBT_RETURN   -4
#define NUM_STANDARD_TARGETS   4

#define EBT_ENTRY_OR_ENTRIES 0x01
/* these are the normal masks */
#define EBT_NOPROTO 0x02
#define EBT_802_3 0x04
#define EBT_SOURCEMAC 0x08
#define EBT_DESTMAC 0x10
#define EBT_F_MASK (EBT_NOPROTO | EBT_802_3 | EBT_SOURCEMAC | EBT_DESTMAC \
   | EBT_ENTRY_OR_ENTRIES)

#define EBT_IPROTO 0x01
#define EBT_IIN 0x02
#define EBT_IOUT 0x04
#define EBT_ISOURCE 0x8
#define EBT_IDEST 0x10
#define EBT_ILOGICALIN 0x20
#define EBT_ILOGICALOUT 0x40
#define EBT_INV_MASK (EBT_IPROTO | EBT_IIN | EBT_IOUT | EBT_ILOGICALIN \
   | EBT_ILOGICALOUT | EBT_ISOURCE | EBT_IDEST)

/* ebtables target modules store the verdict inside an int. We can
 * reclaim a part of this int for backwards compatible extensions.
 * The 4 lsb are more than enough to store the verdict.
 */
#define EBT_VERDICT_BITS 0x0000000F

struct nftnl_rule;
struct iptables_command_state;

static const char *ebt_standard_targets[NUM_STANDARD_TARGETS] = {
	"ACCEPT",
	"DROP",
	"CONTINUE",
	"RETURN",
};

static inline const char *nft_ebt_standard_target(unsigned int num)
{
	if (num >= NUM_STANDARD_TARGETS)
		return NULL;

	return ebt_standard_targets[num];
}

static inline int ebt_fill_target(const char *str, unsigned int *verdict)
{
	int i, ret = 0;

	for (i = 0; i < NUM_STANDARD_TARGETS; i++) {
		if (!strcmp(str, nft_ebt_standard_target(i))) {
			*verdict = -i - 1;
			break;
		}
	}

	if (i == NUM_STANDARD_TARGETS)
		ret = 1;

	return ret;
}

static inline const char *ebt_target_name(unsigned int verdict)
{
	return nft_ebt_standard_target(-verdict - 1);
}

#define EBT_CHECK_OPTION(flags, mask) ({			\
	if (*flags & mask)					\
		xtables_error(PARAMETER_PROBLEM,		\
			      "Multiple use of same "		\
			      "option not allowed");		\
	*flags |= mask;						\
})								\

void ebt_cs_clean(struct iptables_command_state *cs);
void ebt_load_match_extensions(void);
void ebt_add_match(struct xtables_match *m,
			  struct iptables_command_state *cs);
void ebt_add_watcher(struct xtables_target *watcher,
                     struct iptables_command_state *cs);
int ebt_command_default(struct iptables_command_state *cs);

struct nft_among_pair {
	struct ether_addr ether;
	struct in_addr in __attribute__((aligned (4)));
};

struct nft_among_data {
	struct {
		size_t cnt;
		bool inv;
		bool ip;
	} src, dst;
	/* first source, then dest pairs */
	struct nft_among_pair pairs[0];
};

/* initialize fields, return offset into pairs array to write pairs to */
static inline size_t
nft_among_prepare_data(struct nft_among_data *data, bool dst,
		       size_t cnt, bool inv, bool ip)
{
	size_t poff;

	if (dst) {
		data->dst.cnt = cnt;
		data->dst.inv = inv;
		data->dst.ip = ip;
		poff = data->src.cnt;
	} else {
		data->src.cnt = cnt;
		data->src.inv = inv;
		data->src.ip = ip;
		poff = 0;
		memmove(data->pairs + cnt, data->pairs,
			data->dst.cnt * sizeof(*data->pairs));
	}
	return poff;
}

static inline void
nft_among_insert_pair(struct nft_among_pair *pairs,
		      size_t *pcount, const struct nft_among_pair *new)
{
	int i;

	/* nftables automatically sorts set elements from smallest to largest,
	 * insert sorted so extension comparison works */

	for (i = 0; i < *pcount; i++) {
		if (memcmp(new, &pairs[i], sizeof(*new)) < 0)
			break;
	}
	memmove(&pairs[i + 1], &pairs[i], sizeof(*pairs) * (*pcount - i));
	memcpy(&pairs[i], new, sizeof(*new));
	(*pcount)++;
}

#endif
