/* SPDX-License-Identifier: GPL-2.0+ */
/*
 * Copyright (c) 2021 Taehee Yoo <ap420073@gmail.com>
 */
#ifndef _NET_AMT_H_
#define _NET_AMT_H_

#include <linux/siphash.h>
#include <linux/jhash.h>
#include <linux/netdevice.h>
#include <net/gro_cells.h>
#include <net/rtnetlink.h>

enum amt_msg_type {
	AMT_MSG_DISCOVERY = 1,
	AMT_MSG_ADVERTISEMENT,
	AMT_MSG_REQUEST,
	AMT_MSG_MEMBERSHIP_QUERY,
	AMT_MSG_MEMBERSHIP_UPDATE,
	AMT_MSG_MULTICAST_DATA,
	AMT_MSG_TEARDOWN,
	__AMT_MSG_MAX,
};

#define AMT_MSG_MAX (__AMT_MSG_MAX - 1)

enum amt_ops {
	/* A*B */
	AMT_OPS_INT,
	/* A+B */
	AMT_OPS_UNI,
	/* A-B */
	AMT_OPS_SUB,
	/* B-A */
	AMT_OPS_SUB_REV,
	__AMT_OPS_MAX,
};

#define AMT_OPS_MAX (__AMT_OPS_MAX - 1)

enum amt_filter {
	AMT_FILTER_FWD,
	AMT_FILTER_D_FWD,
	AMT_FILTER_FWD_NEW,
	AMT_FILTER_D_FWD_NEW,
	AMT_FILTER_ALL,
	AMT_FILTER_NONE_NEW,
	AMT_FILTER_BOTH,
	AMT_FILTER_BOTH_NEW,
	__AMT_FILTER_MAX,
};

#define AMT_FILTER_MAX (__AMT_FILTER_MAX - 1)

enum amt_act {
	AMT_ACT_GMI,
	AMT_ACT_GMI_ZERO,
	AMT_ACT_GT,
	AMT_ACT_STATUS_FWD_NEW,
	AMT_ACT_STATUS_D_FWD_NEW,
	AMT_ACT_STATUS_NONE_NEW,
	__AMT_ACT_MAX,
};

#define AMT_ACT_MAX (__AMT_ACT_MAX - 1)

enum amt_status {
	AMT_STATUS_INIT,
	AMT_STATUS_SENT_DISCOVERY,
	AMT_STATUS_RECEIVED_DISCOVERY,
	AMT_STATUS_SENT_ADVERTISEMENT,
	AMT_STATUS_RECEIVED_ADVERTISEMENT,
	AMT_STATUS_SENT_REQUEST,
	AMT_STATUS_RECEIVED_REQUEST,
	AMT_STATUS_SENT_QUERY,
	AMT_STATUS_RECEIVED_QUERY,
	AMT_STATUS_SENT_UPDATE,
	AMT_STATUS_RECEIVED_UPDATE,
	__AMT_STATUS_MAX,
};

#define AMT_STATUS_MAX (__AMT_STATUS_MAX - 1)

/* Gateway events only */
enum amt_event {
	AMT_EVENT_NONE,
	AMT_EVENT_RECEIVE,
	AMT_EVENT_SEND_DISCOVERY,
	AMT_EVENT_SEND_REQUEST,
	__AMT_EVENT_MAX,
};

struct amt_header {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u8 type:4,
	   version:4;
#elif defined(__BIG_ENDIAN_BITFIELD)
	u8 version:4,
	   type:4;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
} __packed;

struct amt_header_discovery {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u32	type:4,
		version:4,
		reserved:24;
#elif defined(__BIG_ENDIAN_BITFIELD)
	u32	version:4,
		type:4,
		reserved:24;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
	__be32	nonce;
} __packed;

struct amt_header_advertisement {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u32	type:4,
		version:4,
		reserved:24;
#elif defined(__BIG_ENDIAN_BITFIELD)
	u32	version:4,
		type:4,
		reserved:24;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
	__be32	nonce;
	__be32	ip4;
} __packed;

struct amt_header_request {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u32	type:4,
		version:4,
		reserved1:7,
		p:1,
		reserved2:16;
#elif defined(__BIG_ENDIAN_BITFIELD)
	u32	version:4,
		type:4,
		p:1,
		reserved1:7,
		reserved2:16;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
	__be32	nonce;
} __packed;

struct amt_header_membership_query {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u64	type:4,
		version:4,
		reserved:6,
		l:1,
		g:1,
		response_mac:48;
#elif defined(__BIG_ENDIAN_BITFIELD)
	u64	version:4,
		type:4,
		g:1,
		l:1,
		reserved:6,
		response_mac:48;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
	__be32	nonce;
} __packed;

struct amt_header_membership_update {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u64	type:4,
		version:4,
		reserved:8,
		response_mac:48;
#elif defined(__BIG_ENDIAN_BITFIELD)
	u64	version:4,
		type:4,
		reserved:8,
		response_mac:48;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
	__be32	nonce;
} __packed;

struct amt_header_mcast_data {
#if defined(__LITTLE_ENDIAN_BITFIELD)
	u16	type:4,
		version:4,
		reserved:8;
#elif defined(__BIG_ENDIAN_BITFIELD)
	u16	version:4,
		type:4,
		reserved:8;
#else
#error  "Please fix <asm/byteorder.h>"
#endif
} __packed;

struct amt_headers {
	union {
		struct amt_header_discovery discovery;
		struct amt_header_advertisement advertisement;
		struct amt_header_request request;
		struct amt_header_membership_query query;
		struct amt_header_membership_update update;
		struct amt_header_mcast_data data;
	};
} __packed;

struct amt_gw_headers {
	union {
		struct amt_header_discovery discovery;
		struct amt_header_request request;
		struct amt_header_membership_update update;
	};
} __packed;

struct amt_relay_headers {
	union {
		struct amt_header_advertisement advertisement;
		struct amt_header_membership_query query;
		struct amt_header_mcast_data data;
	};
} __packed;

struct amt_skb_cb {
	struct amt_tunnel_list *tunnel;
};

struct amt_tunnel_list {
	struct list_head	list;
	/* Protect All resources under an amt_tunne_list */
	spinlock_t		lock;
	struct amt_dev		*amt;
	u32			nr_groups;
	u32			nr_sources;
	enum amt_status		status;
	struct delayed_work	gc_wq;
	__be16			source_port;
	__be32			ip4;
	__be32			nonce;
	siphash_key_t		key;
	u64			mac:48,
				reserved:16;
	struct rcu_head		rcu;
	struct hlist_head	groups[];
};

union amt_addr {
	__be32			ip4;
#if IS_ENABLED(CONFIG_IPV6)
	struct in6_addr		ip6;
#endif
};

/* RFC 3810
 *
 * When the router is in EXCLUDE mode, the router state is represented
 * by the notation EXCLUDE (X,Y), where X is called the "Requested List"
 * and Y is called the "Exclude List".  All sources, except those from
 * the Exclude List, will be forwarded by the router
 */
enum amt_source_status {
	AMT_SOURCE_STATUS_NONE,
	/* Node of Requested List */
	AMT_SOURCE_STATUS_FWD,
	/* Node of Exclude List */
	AMT_SOURCE_STATUS_D_FWD,
};

/* protected by gnode->lock */
struct amt_source_node {
	struct hlist_node	node;
	struct amt_group_node	*gnode;
	struct delayed_work     source_timer;
	union amt_addr		source_addr;
	enum amt_source_status	status;
#define AMT_SOURCE_OLD	0
#define AMT_SOURCE_NEW	1
	u8			flags;
	struct rcu_head		rcu;
};

/* Protected by amt_tunnel_list->lock */
struct amt_group_node {
	struct amt_dev		*amt;
	union amt_addr		group_addr;
	union amt_addr		host_addr;
	bool			v6;
	u8			filter_mode;
	u32			nr_sources;
	struct amt_tunnel_list	*tunnel_list;
	struct hlist_node	node;
	struct delayed_work     group_timer;
	struct rcu_head		rcu;
	struct hlist_head	sources[];
};

#define AMT_MAX_EVENTS	16
struct amt_events {
	enum amt_event event;
	struct sk_buff *skb;
};

struct amt_dev {
	struct net_device       *dev;
	struct net_device       *stream_dev;
	struct net		*net;
	/* Global lock for amt device */
	spinlock_t		lock;
	/* Used only in relay mode */
	struct list_head        tunnel_list;
	struct gro_cells	gro_cells;

	/* Protected by RTNL */
	struct delayed_work     discovery_wq;
	/* Protected by RTNL */
	struct delayed_work     req_wq;
	/* Protected by RTNL */
	struct delayed_work     secret_wq;
	struct work_struct	event_wq;
	/* AMT status */
	enum amt_status		status;
	/* Generated key */
	siphash_key_t		key;
	struct sock	  __rcu *sk;
	u32			max_groups;
	u32			max_sources;
	u32			hash_buckets;
	u32			hash_seed;
	/* Default 128 */
	u32                     max_tunnels;
	/* Default 128 */
	u32                     nr_tunnels;
	/* Gateway or Relay mode */
	u32                     mode;
	/* Default 2268 */
	__be16			relay_port;
	/* Default 2268 */
	__be16			gw_port;
	/* Outer local ip */
	__be32			local_ip;
	/* Outer remote ip */
	__be32			remote_ip;
	/* Outer discovery ip */
	__be32			discovery_ip;
	/* Only used in gateway mode */
	__be32			nonce;
	/* Gateway sent request and received query */
	bool			ready4;
	bool			ready6;
	u8			req_cnt;
	u8			qi;
	u64			qrv;
	u64			qri;
	/* Used only in gateway mode */
	u64			mac:48,
				reserved:16;
	/* AMT gateway side message handler queue */
	struct amt_events	events[AMT_MAX_EVENTS];
	u8			event_idx;
	u8			nr_events;
};

#define AMT_TOS			0xc0
#define AMT_IPHDR_OPTS		4
#define AMT_IP6HDR_OPTS		8
#define AMT_GC_INTERVAL		(30 * 1000)
#define AMT_MAX_GROUP		32
#define AMT_MAX_SOURCE		128
#define AMT_HSIZE_SHIFT		8
#define AMT_HSIZE		(1 << AMT_HSIZE_SHIFT)

#define AMT_DISCOVERY_TIMEOUT	5000
#define AMT_INIT_REQ_TIMEOUT	1
#define AMT_INIT_QUERY_INTERVAL	125
#define AMT_MAX_REQ_TIMEOUT	120
#define AMT_MAX_REQ_COUNT	3
#define AMT_SECRET_TIMEOUT	60000
#define IANA_AMT_UDP_PORT	2268
#define AMT_MAX_TUNNELS         128
#define AMT_MAX_REQS		128
#define AMT_GW_HLEN (sizeof(struct iphdr) + \
		     sizeof(struct udphdr) + \
		     sizeof(struct amt_gw_headers))
#define AMT_RELAY_HLEN (sizeof(struct iphdr) + \
		     sizeof(struct udphdr) + \
		     sizeof(struct amt_relay_headers))

static inline bool netif_is_amt(const struct net_device *dev)
{
	return dev->rtnl_link_ops && !strcmp(dev->rtnl_link_ops->kind, "amt");
}

static inline u64 amt_gmi(const struct amt_dev *amt)
{
	return ((amt->qrv * amt->qi) + amt->qri) * 1000;
}

#endif /* _NET_AMT_H_ */
