/* Global definitions for OsmoUPF */
/*
 * (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 * All Rights Reserved.
 *
 * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
 *
 * SPDX-License-Identifier: GPL-2.0+
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#pragma once

#include <osmocom/core/tdef.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/select.h>
#include <osmocom/core/linuxlist.h>
#include <osmocom/core/hashtable.h>

struct osmo_tdef;
struct ctrl_handle;

struct upf_gtp_dev;
struct nft_ctx;

#define UPF_PFCP_LISTEN_DEFAULT "0.0.0.0"

#define PORT_GTP0_C 3386
#define PORT_GTP0_U 3386

#define PORT_GTP1_C 2123
#define PORT_GTP1_U 2152

extern struct osmo_tdef_group g_upf_tdef_groups[];
extern struct osmo_tdef g_upf_nft_tdefs[];

struct pfcp_vty_cfg {
	char *local_addr;
	uint16_t local_port;
};

struct tunend_vty_cfg_dev {
	struct llist_head entry;

	/* If true, osmo-upf creates the GTP device on startup. If false, the GTP device was created by the user, and we
	 * just plug into it. */
	bool create;

	/* GTP device name as shown by 'ip link', e.g. 'apn0' */
	char *dev_name;

	/* Which address the GTP device should listen on.
	 * When create == true, the GTP kernel module is created to listen on this address.
	 * Also used to listen for GTP ECHO requests using libgtp. */
	char *local_addr;
};

struct tunend_vty_cfg {
	/* list of struct tunend_vty_cfg_dev, GTP devices as in the config file. The actual GTP devices in use are in
	 * g_upf->tunend.devs. */
	struct llist_head devs;
};

/* Item in an llist of string pointers */
struct string_listitem {
	struct llist_head entry;
	char *str;
};

struct g_upf {
	struct ctrl_handle *ctrl;

	struct {
		struct pfcp_vty_cfg vty_cfg;
		struct up_endpoint *ep;
	} pfcp;

	/* Tunnel encaps decaps via GTP kernel module */
	struct {
		/* if true, don't actually send commands to the GTP kernel module, just return success. */
		bool mockup;

		/* GTP devices as in osmo-upf.cfg */
		struct tunend_vty_cfg vty_cfg;

		/* GTP devices actually in use, list of struct upf_gtp_dev. */
		struct llist_head devs;

		struct mnl_socket *nl;
		int32_t genl_id;

		uint8_t recovery_count;
	} tunend;

	/* Tunnel forwarding via linux netfilter */
	struct {
		/* if true, don't actually send commands to nftables, just return success. */
		bool mockup;

		struct nft_ctx *nft_ctx;
		char *table_name;
		int priority_pre;
		int priority_post;
		uint32_t next_chain_id_state;
		/* hashtable of (struct upf_nft_tun)->node_by_chain_id: */
		DECLARE_HASHTABLE(nft_tun_by_chain_id, 12);
	} tunmap;

	struct {
		uint32_t next_local_teid_state;
		/* hashtable of (struct pdr)->node_by_local_f_teid: */
		DECLARE_HASHTABLE(pdrs_by_local_f_teid, 12);
		uint16_t next_echo_seq_nr;
	} gtp;

	struct llist_head netinst;
};

extern struct g_upf *g_upf;

enum upf_log_subsys {
	DREF,
	DPEER,
	DSESSION,
	DGTP,
	DNFT,
};

void g_upf_alloc(void *ctx);
void upf_vty_init();
int upf_pfcp_init(void);
int upf_pfcp_listen(void);

int upf_gtp_devs_open();
void upf_gtp_devs_close();

uint32_t upf_next_local_teid(void);
uint32_t upf_next_chain_id(void);
