/* SPDX-License-Identifier: GPL-2.0-only */

#ifndef __NET_PSP_H
#define __NET_PSP_H

#include <linux/mutex.h>
#include <linux/refcount.h>

struct netlink_ext_ack;

#define PSP_DEFAULT_UDP_PORT	1000

struct psphdr {
	u8	nexthdr;
	u8	hdrlen;
	u8	crypt_offset;
	u8	verfl;
	__be32	spi;
	__be64	iv;
	__be64	vc[]; /* optional */
};

#define PSP_ENCAP_HLEN (sizeof(struct udphdr) + sizeof(struct psphdr))

#define PSP_SPI_KEY_ID		GENMASK(30, 0)
#define PSP_SPI_KEY_PHASE	BIT(31)

#define PSPHDR_CRYPT_OFFSET	GENMASK(5, 0)

#define PSPHDR_VERFL_SAMPLE	BIT(7)
#define PSPHDR_VERFL_DROP	BIT(6)
#define PSPHDR_VERFL_VERSION	GENMASK(5, 2)
#define PSPHDR_VERFL_VIRT	BIT(1)
#define PSPHDR_VERFL_ONE	BIT(0)

#define PSP_HDRLEN_NOOPT	((sizeof(struct psphdr) - 8) / 8)

/**
 * struct psp_dev_config - PSP device configuration
 * @versions: PSP versions enabled on the device
 */
struct psp_dev_config {
	u32 versions;
};

/**
 * struct psp_dev - PSP device struct
 * @main_netdev: original netdevice of this PSP device
 * @ops:	driver callbacks
 * @caps:	device capabilities
 * @drv_priv:	driver priv pointer
 * @lock:	instance lock, protects all fields
 * @refcnt:	reference count for the instance
 * @id:		instance id
 * @generation:	current generation of the device key
 * @config:	current device configuration
 * @active_assocs:	list of registered associations
 * @prev_assocs:	associations which use old (but still usable)
 *			device key
 * @stale_assocs:	associations which use a rotated out key
 *
 * @stats:	statistics maintained by the core
 * @stats.rotations:	See stats attr key-rotations
 * @stats.stales:	See stats attr stale-events
 *
 * @rcu:	RCU head for freeing the structure
 */
struct psp_dev {
	struct net_device *main_netdev;

	struct psp_dev_ops *ops;
	struct psp_dev_caps *caps;
	void *drv_priv;

	struct mutex lock;
	refcount_t refcnt;

	u32 id;

	u8 generation;

	struct psp_dev_config config;

	struct list_head active_assocs;
	struct list_head prev_assocs;
	struct list_head stale_assocs;

	struct {
		unsigned long rotations;
		unsigned long stales;
	} stats;

	struct rcu_head rcu;
};

#define PSP_GEN_VALID_MASK	0x7f

/**
 * struct psp_dev_caps - PSP device capabilities
 */
struct psp_dev_caps {
	/**
	 * @versions: mask of supported PSP versions
	 * Set this field to 0 to indicate PSP is not supported at all.
	 */
	u32 versions;

	/**
	 * @assoc_drv_spc: size of driver-specific state in Tx assoc
	 * Determines the size of struct psp_assoc::drv_data
	 */
	u32 assoc_drv_spc;
};

#define PSP_MAX_KEY	32

#define PSP_HDR_SIZE	16	/* We don't support optional fields, yet */
#define PSP_TRL_SIZE	16	/* AES-GCM/GMAC trailer size */

struct psp_skb_ext {
	__be32 spi;
	u16 dev_id;
	u8 generation;
	u8 version;
};

struct psp_key_parsed {
	__be32 spi;
	u8 key[PSP_MAX_KEY];
};

struct psp_assoc {
	struct psp_dev *psd;

	u16 dev_id;
	u8 generation;
	u8 version;
	u8 peer_tx;

	u32 upgrade_seq;

	struct psp_key_parsed tx;
	struct psp_key_parsed rx;

	refcount_t refcnt;
	struct rcu_head rcu;
	struct work_struct work;
	struct list_head assocs_list;

	u8 drv_data[] __aligned(8);
};

struct psp_dev_stats {
	union {
		struct {
			u64 rx_packets;
			u64 rx_bytes;
			u64 rx_auth_fail;
			u64 rx_error;
			u64 rx_bad;
			u64 tx_packets;
			u64 tx_bytes;
			u64 tx_error;
		};
		DECLARE_FLEX_ARRAY(u64, required);
	};
};

/**
 * struct psp_dev_ops - netdev driver facing PSP callbacks
 */
struct psp_dev_ops {
	/**
	 * @set_config: set configuration of a PSP device
	 * Driver can inspect @psd->config for the previous configuration.
	 * Core will update @psd->config with @config on success.
	 */
	int (*set_config)(struct psp_dev *psd, struct psp_dev_config *conf,
			  struct netlink_ext_ack *extack);

	/**
	 * @key_rotate: rotate the device key
	 */
	int (*key_rotate)(struct psp_dev *psd, struct netlink_ext_ack *extack);

	/**
	 * @rx_spi_alloc: allocate an Rx SPI+key pair
	 * Allocate an Rx SPI and resulting derived key.
	 * This key should remain valid until key rotation.
	 */
	int (*rx_spi_alloc)(struct psp_dev *psd, u32 version,
			    struct psp_key_parsed *assoc,
			    struct netlink_ext_ack *extack);

	/**
	 * @tx_key_add: add a Tx key to the device
	 * Install an association in the device. Core will allocate space
	 * for the driver to use at drv_data.
	 */
	int (*tx_key_add)(struct psp_dev *psd, struct psp_assoc *pas,
			  struct netlink_ext_ack *extack);
	/**
	 * @tx_key_del: remove a Tx key from the device
	 * Remove an association from the device.
	 */
	void (*tx_key_del)(struct psp_dev *psd, struct psp_assoc *pas);

	/**
	 * @get_stats: get statistics from the device
	 * Stats required by the spec must be maintained and filled in.
	 * Stats must be filled in member-by-member, never memset the struct.
	 */
	void (*get_stats)(struct psp_dev *psd, struct psp_dev_stats *stats);
};

#endif /* __NET_PSP_H */
